I am currently working on a C++ application that will read the message from the MSMQ.....
this is the code that I ran :
HRESULT ReadingDestQueue(
WCHAR * wszQueueName,
WCHAR * wszComputerName
)
{
// Define the required constants and variables.
const int NUMBEROFPROPERTIES = 5;
DWORD cPropId = 0;
HRESULT hr = MQ_OK; // Return code
HANDLE hQueue = NULL; // Queue handle
ULONG ulBufferSize = 2;
// Define an MQMSGPROPS structure.
MQMSGPROPS msgprops;
MSGPROPID aMsgPropId[NUMBEROFPROPERTIES];
MQPROPVARIANT aMsgPropVar[NUMBEROFPROPERTIES];
HRESULT aMsgStatus[NUMBEROFPROPERTIES];
// Specify the message properties to be retrieved.
aMsgPropId[cPropId] = PROPID_M_LABEL_LEN; // Property ID
aMsgPropVar[cPropId].vt = VT_UI4; // Type indicator
aMsgPropVar[cPropId].ulVal = MQ_MAX_MSG_LABEL_LEN; // Length of label
cPropId++;
WCHAR wszLabelBuffer[MQ_MAX_MSG_LABEL_LEN]; // Label buffer
aMsgPropId[cPropId] = PROPID_M_LABEL; // Property ID
aMsgPropVar[cPropId].vt = VT_LPWSTR; // Type indicator
aMsgPropVar[cPropId].pwszVal = wszLabelBuffer; // Label buffer
cPropId++;
UCHAR * pucBodyBuffer = NULL;
pucBodyBuffer = (UCHAR*)malloc(ulBufferSize);
if (pucBodyBuffer == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(pucBodyBuffer, 0, ulBufferSize);
aMsgPropId[cPropId] = PROPID_M_BODY_SIZE; // Property ID
aMsgPropVar[cPropId].vt = VT_NULL; // Type indicator
cPropId++;
aMsgPropId[cPropId] = PROPID_M_BODY; // Property ID
aMsgPropVar[cPropId].vt = VT_VECTOR | VT_UI1; // Type indicator
aMsgPropVar[cPropId].caub.pElems = (UCHAR*)pucBodyBuffer; // Body buffer
aMsgPropVar[cPropId].caub.cElems = ulBufferSize; // Buffer size
cPropId++;
aMsgPropId[cPropId] = PROPID_M_BODY_TYPE; // Property ID
aMsgPropVar[cPropId].vt = VT_NULL; // Type indicator
cPropId++;
// Initialize the MQMSGPROPS structure.
msgprops.cProp = cPropId; // Number of message properties
msgprops.aPropID = aMsgPropId; // IDs of the message properties
msgprops.aPropVar = aMsgPropVar; // Values of the message properties
msgprops.aStatus = aMsgStatus; // Error reports
// Validate the input strings.
if (wszQueueName == NULL || wszComputerName == NULL)
{
return MQ_ERROR_INVALID_PARAMETER;
}
// Create a direct format name.
WCHAR * wszFormatName = NULL;
DWORD dwFormatNameLength = 0;
dwFormatNameLength = wcslen(wszQueueName) + wcslen(wszComputerName) + 12;
wszFormatName = new WCHAR[dwFormatNameLength];
if (wszFormatName == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(wszFormatName, 0, dwFormatNameLength);
// ************************************
// You must concatenate "DIRECT=OS:", wszComputerName, "\",
// and wszQueueName into the wszFormatName buffer.
// wszFormatName = "DIRECT=OS:" + wszComputerName + "\" +
// wszQueueName
// If the format name is too long for the buffer, return FALSE.
// ************************************
// Open the queue with receive access.
hr = MQOpenQueue(
wszFormatName, // Format name of the queue
MQ_RECEIVE_ACCESS, // Access mode
MQ_DENY_NONE, // Share mode
&hQueue // OUT: Queue handle
);
// Free the memory that was allocated for the format name string.
if (wszFormatName)
{
delete[] wszFormatName;
}
// Handle any error returned by MQOpenQueue.
if (FAILED(hr))
{
return hr;
}
for (; ; )
{
aMsgPropVar[0].ulVal = MQ_MAX_MSG_LABEL_LEN;
hr = MQReceiveMessage(
hQueue, // Queue handle
1000, // Max time to (msec) to receive the message
MQ_ACTION_RECEIVE, // Receive action
&msgprops, // Message property structure
NULL, // No OVERLAPPED structure
NULL, // No callback function
NULL, // No cursor handle
MQ_NO_TRANSACTION // Not in a transaction
);
if (hr == MQ_ERROR_BUFFER_OVERFLOW)
{
ulBufferSize = aMsgPropVar[2].ulVal * sizeof(UCHAR);
pucBodyBuffer = (UCHAR*)realloc(pucBodyBuffer, ulBufferSize);
if (pucBodyBuffer == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(pucBodyBuffer, 0, ulBufferSize);
aMsgPropVar[3].caub.pElems = (UCHAR*)pucBodyBuffer;
aMsgPropVar[3].caub.cElems = ulBufferSize;
continue;
}
if (FAILED(hr))
{
wprintf(L"No messages. Closing queue\n");
break;
}
// If the message contains a label, print it.
if (msgprops.aPropVar[0].ulVal == 0)
{
wprintf(L"Removed message from queue.\n");
}
else
{
wprintf(L"Removed message '%s' from queue.\n", wszLabelBuffer);
}
// If the message body is a string, display it.
if (msgprops.aPropVar[4].ulVal == VT_BSTR)
{
wprintf(L"Body: %s", (WCHAR*)pucBodyBuffer);
wprintf(L"\n");
}
}
// Close the queue and free the memory allocated for the body buffer.
hr = MQCloseQueue(hQueue);
free(pucBodyBuffer);
return hr;
}
int main()
{
WCHAR wszComputerName =(WCHAR)L"wsm-ela-inc5";
WCHAR wszQueueName = (WCHAR)L"private$\\soorya1";
_com_error err(ReadingDestQueue(&wszQueueName, &wszComputerName));
LPCTSTR errMsg = err.ErrorMessage();
wcout << errMsg;
return 0;
}
this is my MSMQ queue :
when I run this...I am getting an error code of "0xC00E001E" (MQ_ERROR_ILLEGAL_FORMATNAME (0xC00E001E)
Returned when the specified format name is not valid)
any methods to recover this?
Passing the format name itself did the trick!
here is the code :
// ReadMsg.cpp : Defines the entry point for the console application.
//
#define _CRT_SECURE_NO_DEPRECATE
#include "stdafx.h"
#include "windows.h"
#include "mq.h"
#include "tchar.h"
#include"comdef.h"
#include"iostream"
using namespace std;
HRESULT ReadingDestQueue( LPWSTR lpwcsFormatName)
{
// Define the required constants and variables.
const int NUMBEROFPROPERTIES = 5;
DWORD cPropId = 0;
HRESULT hr = MQ_OK; // Return code
HANDLE hQueue = NULL; // Queue handle
ULONG ulBufferSize = 2;
// Define an MQMSGPROPS structure.
MQMSGPROPS msgprops;
MSGPROPID aMsgPropId[NUMBEROFPROPERTIES];
MQPROPVARIANT aMsgPropVar[NUMBEROFPROPERTIES];
HRESULT aMsgStatus[NUMBEROFPROPERTIES];
// Specify the message properties to be retrieved.
aMsgPropId[cPropId] = PROPID_M_LABEL_LEN; // Property ID
aMsgPropVar[cPropId].vt = VT_UI4; // Type indicator
aMsgPropVar[cPropId].ulVal = MQ_MAX_MSG_LABEL_LEN; // Length of label
cPropId++;
WCHAR wszLabelBuffer[MQ_MAX_MSG_LABEL_LEN]; // Label buffer
aMsgPropId[cPropId] = PROPID_M_LABEL; // Property ID
aMsgPropVar[cPropId].vt = VT_LPWSTR; // Type indicator
aMsgPropVar[cPropId].pwszVal = wszLabelBuffer; // Label buffer
cPropId++;
UCHAR * pucBodyBuffer = NULL;
pucBodyBuffer = (UCHAR*)malloc(ulBufferSize);
if (pucBodyBuffer == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(pucBodyBuffer, 0, ulBufferSize);
aMsgPropId[cPropId] = PROPID_M_BODY_SIZE; // Property ID
aMsgPropVar[cPropId].vt = VT_NULL; // Type indicator
cPropId++;
aMsgPropId[cPropId] = PROPID_M_BODY; // Property ID
aMsgPropVar[cPropId].vt = VT_VECTOR | VT_UI1; // Type indicator
aMsgPropVar[cPropId].caub.pElems = (UCHAR*)pucBodyBuffer; // Body buffer
aMsgPropVar[cPropId].caub.cElems = ulBufferSize; // Buffer size
cPropId++;
aMsgPropId[cPropId] = PROPID_M_BODY_TYPE; // Property ID
aMsgPropVar[cPropId].vt = VT_NULL; // Type indicator
cPropId++;
// Initialize the MQMSGPROPS structure.
msgprops.cProp = cPropId; // Number of message properties
msgprops.aPropID = aMsgPropId; // IDs of the message properties
msgprops.aPropVar = aMsgPropVar; // Values of the message properties
msgprops.aStatus = aMsgStatus; // Error reports
// Validate the input strings.
if (lpwcsFormatName == NULL)
{
return MQ_ERROR_INVALID_PARAMETER;
}
// Open the queue with receive access.
hr = MQOpenQueue(
lpwcsFormatName, // Format name of the queue
MQ_RECEIVE_ACCESS, // Access mode
MQ_DENY_NONE, // Share mode
&hQueue // OUT: Queue handle
);
// Handle any error returned by MQOpenQueue.
if (FAILED(hr))
{
return hr;
}
while(1)
{
aMsgPropVar[0].ulVal = MQ_MAX_MSG_LABEL_LEN;
hr = MQReceiveMessage(
hQueue, // Queue handle
1000, // Max time to (msec) to receive the message
MQ_ACTION_RECEIVE, // Receive action
&msgprops, // Message property structure
NULL, // No OVERLAPPED structure
NULL, // No callback function
NULL, // No cursor handle
MQ_NO_TRANSACTION // Not in a transaction
);
if (hr == MQ_ERROR_BUFFER_OVERFLOW)
{
ulBufferSize = aMsgPropVar[2].ulVal * sizeof(UCHAR);
pucBodyBuffer = (UCHAR*)realloc(pucBodyBuffer, ulBufferSize);
if (pucBodyBuffer == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(pucBodyBuffer, 0, ulBufferSize);
aMsgPropVar[3].caub.pElems = (UCHAR*)pucBodyBuffer;
aMsgPropVar[3].caub.cElems = ulBufferSize;
continue;
}
if (FAILED(hr))
{
wprintf(L"No messages. Closing queue\n");
break;
}
// If the message contains a label, print it.
if (msgprops.aPropVar[0].ulVal == 0)
{
wprintf(L"Removed message from queue.\n");
}
else
{
wprintf(L"Removed message '%s' from queue.\n", (char *)wszLabelBuffer);
//cout << "Removed message ";
//wcout << wszLabelBuffer;
//cout << "from queue.\n";
}
// If the message body is a string, display it.
if (msgprops.aPropVar[4].ulVal == VT_BSTR)
{
wprintf(L"Body: %s", (WCHAR*)pucBodyBuffer);
wprintf(L"\n");
}
}
// Close the queue and free the memory allocated for the body buffer.
hr = MQCloseQueue(hQueue);
free(pucBodyBuffer);
return hr;
}
int main()
{
LPWSTR lpwcsFormatName = (LPWSTR)L"DIRECT=OS:wsm-ela-inc5\\private$\\soorya97";
_com_error err(ReadingDestQueue(lpwcsFormatName));
LPCTSTR errMsg = err.ErrorMessage();
wcout << errMsg << endl;
return 0;
}
Related
MQSendMessage() returns value of S_OK even when the message is not sent.
Is there a way to make sure the message was sent?
According to message queue storage limit on a single queue doesn't work? the function will always return S_OK (unless some obvious error, like wrong parameters) even when the message cannot be sent.
From the documentation for MQSendMessage:
Information on errors that occur after the message is placed in an outgoing queue is provided by acknowledgment messages.
The API expects you to request an acknowledgement message, receive it later on, and match it to the original using its PROPID_M_CORRELATIONID property. This page provides an example:
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms700122%28v%3dvs.85%29
EDIT: From the link:
HRESULT MatchAck(
LPCWSTR wszAdminQueueName,
LPCWSTR wszComputerName,
UCHAR * rgucMsgID //Array of bytes
)
{
// Validate the input strings.
if (wszAdminQueueName == NULL || wszComputerName == NULL)
{
return MQ_ERROR_INVALID_PARAMETER;
}
// Define the required constants and variables.
const int NUMBEROFPROPERTIES = 3; // Number of properties
DWORD cPropId = 0; // Property counter
WCHAR wszLabelBuffer[MQ_MAX_MSG_LABEL_LEN]; // Message label buffer
// Define an MQMSGPROPS structure.
MQMSGPROPS msgprops;
MSGPROPID aMsgPropId[NUMBEROFPROPERTIES];
PROPVARIANT aMsgPropVar[NUMBEROFPROPERTIES];
HRESULT aMsgStatus[NUMBEROFPROPERTIES];
HANDLE hQueue = NULL; // Queue handle
HANDLE hCursor = NULL; // Cursor handle
HRESULT hr = MQ_OK; // Return code
int i = 0; // Array index
// Create a buffer of unsigned characters of length
// PROPID_M_CORRELATIONID_SIZE, which is equal to 20, and specify
// PROPID_M_CORRELATIONID as a message property to be retrieved.
UCHAR rgucCorrelationID[PROPID_M_CORRELATIONID_SIZE];
aMsgPropId[cPropId] = PROPID_M_CORRELATIONID; // Property ID
aMsgPropVar[cPropId].vt = VT_VECTOR | VT_UI1 ; // Type indicator
aMsgPropVar[cPropId].caub.pElems = rgucCorrelationID;
aMsgPropVar[cPropId].caub.cElems = PROPID_M_CORRELATIONID_SIZE;
cPropId++;
// Specify the message label and its length as message properties to be retrieved.
aMsgPropId[cPropId] = PROPID_M_LABEL_LEN; // Property ID
aMsgPropVar[cPropId].vt = VT_UI4; // Type indicator
aMsgPropVar[cPropId].ulVal = MQ_MAX_MSG_LABEL_LEN; // Label buffer size
cPropId++;
aMsgPropId[cPropId] = PROPID_M_LABEL; // Property ID
aMsgPropVar[cPropId].vt = VT_LPWSTR; // Type indicator
aMsgPropVar[cPropId].pwszVal = wszLabelBuffer; // Label buffer
cPropId++;
// Initialize the MQMSGPROPS structure.
msgprops.cProp = cPropId; // Number of message properties
msgprops.aPropID = aMsgPropId; // IDs of the message properties
msgprops.aPropVar = aMsgPropVar; // Values of the message properties
msgprops.aStatus = aMsgStatus; // Error reports
// Generate the format name required for opening the administration queue.
WCHAR * wszAdminFormatName = NULL;
DWORD dwFormatNameLength = 0;
dwFormatNameLength = wcslen(wszAdminQueueName) + wcslen(wszComputerName) + 12;
wszAdminFormatName = new WCHAR[dwFormatNameLength];
if (wszAdminFormatName == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(wszAdminFormatName, 0, dwFormatNameLength*sizeof(WCHAR));
// ************************************
// You must concatenate "DIRECT=OS:", wszComputerName, "\",
// and wszAdminQueueName into the wszAdminFormatName buffer.
// wszAdminFormatName = "DIRECT=OS:" + wszComputerName + "\" +
// wszAdminQueueName
// If the format name is too long for the buffer,
// return FALSE.
// ************************************
// Open the administration queue to peek at all the messages.
hr = MQOpenQueue(
wszAdminFormatName, // Format name of the administration queue
MQ_RECEIVE_ACCESS, // Access mode
MQ_DENY_RECEIVE_SHARE, // Share mode
&hQueue // OUT: Queue handle
);
// Free the memory that was allocated for the format name string.
delete [] wszAdminFormatName;
// Handle any error returned by MQOpenQueue.
if (FAILED(hr))
{
return hr;
}
// Create the cursor used to navigate through the administration queue.
hr = MQCreateCursor(
hQueue, // Queue handle
&hCursor // OUT: Cursor handle
);
if (FAILED(hr))
{
MQCloseQueue(hQueue);
return hr;
}
// Peek at the first message in the administration queue.
hr = MQReceiveMessage(
hQueue, // Queue handle
0, // Maximum time (msec)
MQ_ACTION_PEEK_CURRENT, // Receive action
&msgprops, // Message property structure
NULL, // No OVERLAPPED structure
NULL, // No callback function
hCursor, // Cursor handle
MQ_NO_TRANSACTION // Not in a transaction
);
if (FAILED(hr))
{
MQCloseCursor(hCursor);
MQCloseQueue(hQueue);
return hr;
}
// Find the matching message in the administration queue.
do
{
for (i = 0; (rgucMsgID[i] == rgucCorrelationID[i]) && (i < 20); i++);
if (i == 20) // The message and correlation IDs match.
{
// Display the label of the message.
wprintf(L"Label of the matching message: %s\n", msgprops.aPropVar[2].pwszVal);
}
//Peek at the next message in the administration queue.
hr = MQReceiveMessage(
hQueue, // Queue handle
0, // Maximum time (msec)
MQ_ACTION_PEEK_NEXT, // Receive action set to peek at the next message
&msgprops, // Message property structure
NULL, // No OVERLAPPED structure
NULL, // No callback function
hCursor, // Cursor handle
NULL // No transaction
);
if (FAILED(hr))
{
break;
}
} while (SUCCEEDED(hr));
// Close the cursor and queue to free resources.
hr = MQCloseCursor(hCursor);
if (FAILED(hr))
{
MQCloseQueue(hQueue);
return hr;
}
hr = MQCloseQueue(hQueue);
if (FAILED(hr))
{
return hr;
}
wprintf(L"Peeking completed! Label of the last message: %s\n", msgprops.aPropVar[2].pwszVal);
return hr;
}
I am implementing a Windows Biometric Driver using the umdf sample from github.
When I call WinBioCaptureSample the next plugin's methods run in a loop.
SensorAdapterClearContext
EngineAdapterClearContext
SensorAdapterStartCapture
SensorAdapterFinishCapture
I used TraceView to debug my driver and it shows the next trace messages when is stuck in a loop.
00000001 driver 352840 439560 1 1 04\05\2018-16:46:13:12 CBiometricDevice::OnGetSensorStatus Called.
00000002 driver 352840 439560 1 2 04\05\2018-16:46:13:12 CBiometricDevice::OnGetAttributes Called.
00000003 driver 352840 439560 1 3 04\05\2018-16:46:13:12 CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000004 driver 352840 439560 1 4 04\05\2018-16:46:13:12 CBiometricDevice::OnCaptureData Called.
00000005 driver 352840 439560 4 5 04\05\2018-16:46:13:28 CBiometricDevice::OnGetSensorStatus Called.
00000006 driver 352840 439560 1 6 04\05\2018-16:46:13:29 CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000007 driver 352840 439560 1 7 04\05\2018-16:46:13:29 CBiometricDevice::OnCaptureData Called.
00000008 driver 352840 439560 1 8 04\05\2018-16:46:13:30 CBiometricDevice::OnGetSensorStatus Called.
00000009 driver 352840 439560 4 9 04\05\2018-16:46:13:30 CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000010 driver 352840 439560 1 10 04\05\2018-16:46:13:31 CBiometricDevice::OnCaptureData Called.
...
The method CBiometricDevice::OnGetSensorStatus always returns WINBIO_SENSOR_READY
diagnostics->WinBioHresult = S_OK;
diagnostics->SensorStatus = WINBIO_SENSOR_READY;
MyRequest.SetInformation(diagnostics->PayloadSize);
MyRequest.SetCompletionHr(S_OK);
Next is the method CBiometricDevice::OnCaptureData
DWORD WINAPI
CaptureSleepThread(
LPVOID lpParam
)
{
CBiometricDevice *device = (CBiometricDevice *) lpParam;
PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams();
if (sleepParams->SleepValue > 60)
{
sleepParams->SleepValue = 60;
}
Sleep(sleepParams->SleepValue * 1000);
UCHAR szBuffer[] = { 0x08, 0x01, 0x00, 0x02 };
ULONG cbRead = 4;
sleepParams->captureData->WinBioHresult = S_OK;
sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
sleepParams->captureData->RejectDetail = 0;
sleepParams->captureData->CaptureData.Size = cbRead;
RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);
device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information);
return 0;
}
CBiometricDevice::OnCaptureData(
_Inout_ IWDFIoRequest *FxRequest
)
{
ULONG controlCode = 0;
PWINBIO_CAPTURE_PARAMETERS captureParams = NULL;
SIZE_T inputBufferSize = 0;
PWINBIO_CAPTURE_DATA captureData = NULL;
SIZE_T outputBufferSize = 0;
bool requestPending = false;
EnterCriticalSection(&m_RequestLock);
if (m_PendingRequest == NULL)
{
if (m_SleepThread != INVALID_HANDLE_VALUE)
{
LeaveCriticalSection(&m_RequestLock);
// TODO: Add code to signal thread to exit.
WaitForSingleObject(m_SleepThread, INFINITE);
CloseHandle(m_SleepThread);
m_SleepThread = INVALID_HANDLE_VALUE;
EnterCriticalSection(&m_RequestLock);
}
if (m_PendingRequest == NULL)
{
m_PendingRequest = FxRequest;
m_PendingRequest->MarkCancelable(this);
}
else
{
requestPending = true;
}
}
else
{
requestPending = true;
}
LeaveCriticalSection(&m_RequestLock);
if (requestPending)
{
FxRequest->Complete(WINBIO_E_DATA_COLLECTION_IN_PROGRESS);
return;
}
GetIoRequestParams(FxRequest,
&controlCode,
(PUCHAR *)&captureParams,
&inputBufferSize,
(PUCHAR *)&captureData,
&outputBufferSize);
if (inputBufferSize < sizeof (WINBIO_CAPTURE_PARAMETERS))
{
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Invalid argument(s).");
CompletePendingRequest(E_INVALIDARG, 0);
return;
}
if (outputBufferSize < sizeof(DWORD))
{
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Output buffer NULL or too small to return size information.");
CompletePendingRequest(E_INVALIDARG, 0);
return;
}
if (outputBufferSize < sizeof (WINBIO_CAPTURE_DATA))
{
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Buffer too small - must be at least 0x%x.", sizeof (WINBIO_CAPTURE_DATA));
DWORD cbSize = 262144;//obtained from MAXIMUM_TRANSFER_SIZE policy of WinUsb
captureData->PayloadSize = (DWORD) sizeof(WINBIO_CAPTURE_DATA) + cbSize;
CompletePendingRequest(S_OK, sizeof(DWORD));
return;
}
RtlZeroMemory(captureData, outputBufferSize);
captureData->PayloadSize = (DWORD) outputBufferSize;// (DWORD) sizeof(WINBIO_CAPTURE_DATA);
captureData->WinBioHresult = WINBIO_E_NO_CAPTURE_DATA;
captureData->SensorStatus = WINBIO_SENSOR_FAILURE;
captureData->RejectDetail= 0;
captureData->CaptureData.Size = 0;
if (captureParams->Purpose == WINBIO_NO_PURPOSE_AVAILABLE)
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_PURPOSE;
}
else if ((captureParams->Format.Type != WINBIO_ANSI_381_FORMAT_TYPE) ||
(captureParams->Format.Owner != WINBIO_ANSI_381_FORMAT_OWNER))
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_FORMAT;
}
else if (captureParams->Flags != WINBIO_DATA_FLAG_RAW)
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_TYPE;
}
struct _WINUSB_PIPE_INFORMATION InputPipeInfo, OutputPipeInfo;
m_pIUsbInputPipe->GetInformation(&InputPipeInfo);
m_pIUsbOutputPipe->GetInformation(&OutputPipeInfo);
m_SleepParams.PipeInId = InputPipeInfo.PipeId;
m_SleepParams.PipeOutId = OutputPipeInfo.PipeId;
m_SleepParams.hDeviceHandle = m_pIUsbInterface->GetWinUsbHandle();
m_SleepParams.cbSize = (DWORD) outputBufferSize;
m_SleepParams.SleepValue = 5;
m_SleepParams.Hr = S_OK;
m_SleepParams.Information = captureData->PayloadSize;
m_SleepParams.captureData = captureData;
m_SleepThread = CreateThread(NULL, // default security attributes
0, // use default stack size
CaptureSleepThread, // thread function name
this, // argument to thread function
0, // use default creation flags
NULL); // returns the thread identifier
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC! Called.");
}
The methods SensorAdapterStartCapture and SensorAdapterFinishCapture returns S_OK
static HRESULT
WINAPI
SensorAdapterStartCapture(
_Inout_ PWINBIO_PIPELINE Pipeline,
_In_ WINBIO_BIR_PURPOSE Purpose,
_Out_ LPOVERLAPPED *Overlapped
)
{
HRESULT hr = S_OK;
WINBIO_SENSOR_STATUS sensorStatus = WINBIO_SENSOR_FAILURE;
WINBIO_CAPTURE_PARAMETERS captureParameters = { 0 };
BOOL result = TRUE;
DWORD bytesReturned = 0;
// Verify that pointer arguments are not NULL.
if (!ARGUMENT_PRESENT(Pipeline) ||
!ARGUMENT_PRESENT(Purpose) ||
!ARGUMENT_PRESENT(Overlapped))
{
return E_POINTER;
}
// Retrieve the context from the pipeline.
PWINIBIO_SENSOR_CONTEXT sensorContext =
(PWINIBIO_SENSOR_CONTEXT)Pipeline->SensorContext;
// Verify the state of the pipeline.
if (sensorContext == NULL ||
Pipeline->SensorHandle == INVALID_HANDLE_VALUE)
{
return WINBIO_E_INVALID_DEVICE_STATE;
}
*Overlapped = NULL;
// Synchronously retrieve the status.
hr = SensorAdapterQueryStatus(Pipeline, &sensorStatus);
if (FAILED(hr))
{
return hr;
}
// Determine whether the sensor requires calibration.
//if (sensorStatus == WINBIO_SENSOR_NOT_CALIBRATED)
//{
// Call a custom function that sends IOCTLs to
// the sensor to calibrate it. This operation is
// synchronous.
//hr = _SensorAdapterCalibrate(Pipeline);
// Retrieve the status again to determine whether the
// sensor is ready.
//if (SUCCEEDED(hr))
//{
// hr = SensorAdapterQueryStatus(Pipeline, &sensorStatus);
//}
//if (FAILED(hr))
//{
// return hr;
//}
//}
if (sensorStatus == WINBIO_SENSOR_BUSY)
{
return WINBIO_E_DEVICE_BUSY;
}
if (sensorStatus != WINBIO_SENSOR_READY)
{
return WINBIO_E_INVALID_DEVICE_STATE;
}
// Determine whether the data format has been previously determined.
// If it has not, find a format supported by both the engine and
// the sensor.
if ((sensorContext->Format.Owner == 0) &&
(sensorContext->Format.Type == 0))
{
// Retrieve the format preferred by the engine.
hr = Pipeline->EngineInterface->QueryPreferredFormat(
Pipeline,
&sensorContext->Format,
&sensorContext->VendorFormat
);
if (SUCCEEDED(hr))
{
// Call a private function that queries the sensor driver
// and attaches an attribute array to the sensor context.
// This operation is synchronous.
hr = _SensorAdapterGetAttributes(Pipeline);
}
if (SUCCEEDED(hr))
{
// Search the sensor attributes array for the format
// preferred by the engine adapter.
DWORD i = 0;
for (i = 0; i < sensorContext->AttributesBuffer->SupportedFormatEntries; i++)
{
if ((sensorContext->AttributesBuffer->SupportedFormat[i].Owner == sensorContext->Format.Owner) &&
(sensorContext->AttributesBuffer->SupportedFormat[i].Type == sensorContext->Format.Type))
{
break;
}
}
if (i == sensorContext->AttributesBuffer->SupportedFormatEntries)
{
// No match was found. Use the default.
sensorContext->Format.Owner = WINBIO_ANSI_381_FORMAT_OWNER;
sensorContext->Format.Type = WINBIO_ANSI_381_FORMAT_TYPE;
}
}
else
{
return hr;
}
}
// Set up the parameter-input block needed for the IOCTL.
captureParameters.PayloadSize = sizeof(WINBIO_CAPTURE_PARAMETERS);
captureParameters.Purpose = Purpose;
captureParameters.Format.Owner = sensorContext->Format.Owner;
captureParameters.Format.Type = sensorContext->Format.Type;
CopyMemory(&captureParameters.VendorFormat, &sensorContext->VendorFormat, sizeof(WINBIO_UUID));
captureParameters.Flags = WINBIO_DATA_FLAG_RAW;
// Determine whether a buffer has already been allocated for this sensor.
if (sensorContext->CaptureBuffer == NULL)
{
DWORD allocationSize = 0;
sensorContext->CaptureBufferSize = 0;
// This sample assumes that the sensor driver returns
// a fixed-size DWORD buffer containing the required
// size of the capture buffer if it receives a buffer
// that is smaller than sizeof(WINBIO_CAPTURE_DATA).
//
// Call the driver with a small buffer to get the
// allocation size required for this sensor.
//
// Because this operation is asynchronous, you must block
// and wait for it to complete.
result = DeviceIoControl(
Pipeline->SensorHandle,
IOCTL_BIOMETRIC_CAPTURE_DATA,
&captureParameters,
sizeof(WINBIO_CAPTURE_PARAMETERS),
&allocationSize,
sizeof(DWORD),
&bytesReturned,
&sensorContext->Overlapped
);
if (!result && GetLastError() == ERROR_IO_PENDING)
{
SetLastError(ERROR_SUCCESS);
result = GetOverlappedResult(
Pipeline->SensorHandle,
&sensorContext->Overlapped,
&bytesReturned,
TRUE
);
}
if (!result || bytesReturned != sizeof(DWORD))
{
// An error occurred.
hr = _AdapterGetHresultFromWin32(GetLastError());
return hr;
}
// Make sure that you allocate at least the minimum buffer
// size needed to get the payload structure.
if (allocationSize < sizeof(WINBIO_CAPTURE_DATA))
{
allocationSize = sizeof(WINBIO_CAPTURE_DATA);
}
// Allocate the buffer.
sensorContext->CaptureBuffer = (PWINBIO_CAPTURE_DATA)_AdapterAlloc(allocationSize);
if (!sensorContext->CaptureBuffer)
{
sensorContext->CaptureBufferSize = 0;
return E_OUTOFMEMORY;
}
sensorContext->CaptureBufferSize = allocationSize;
}
else
{
// The buffer has already been allocated. Clear the buffer contents.
SensorAdapterClearContext(Pipeline);
}
// Send the capture request. Because this is an asynchronous operation,
// the IOCTL call will return immediately regardless of
// whether the I/O has completed.
result = DeviceIoControl(
Pipeline->SensorHandle,
IOCTL_BIOMETRIC_CAPTURE_DATA,
&captureParameters,
sizeof(WINBIO_CAPTURE_PARAMETERS),
sensorContext->CaptureBuffer,
(DWORD)sensorContext->CaptureBufferSize,
&bytesReturned,
&sensorContext->Overlapped
);
if (result ||
(!result && GetLastError() == ERROR_IO_PENDING))
{
*Overlapped = &sensorContext->Overlapped;
return S_OK;
}
else
{
hr = _AdapterGetHresultFromWin32(GetLastError());
return hr;
}
}
static HRESULT
WINAPI
SensorAdapterFinishCapture(
_Inout_ PWINBIO_PIPELINE Pipeline,
_Out_ PWINBIO_REJECT_DETAIL RejectDetail
)
{
HRESULT hr = S_OK;
//WINBIO_SENSOR_STATUS sensorStatus = WINBIO_SENSOR_FAILURE;
WINBIO_CAPTURE_PARAMETERS captureParameters = { 0 };
BOOL result = TRUE;
DWORD bytesReturned = 0;
// Verify that pointer arguments are not NULL.
if (!ARGUMENT_PRESENT(Pipeline) ||
!ARGUMENT_PRESENT(RejectDetail))
{
return E_POINTER;
}
// Retrieve the context from the pipeline.
PWINIBIO_SENSOR_CONTEXT sensorContext =
(PWINIBIO_SENSOR_CONTEXT)Pipeline->SensorContext;
// Verify the state of the pipeline.
if (sensorContext == NULL ||
Pipeline->SensorHandle == INVALID_HANDLE_VALUE)
{
return WINBIO_E_INVALID_DEVICE_STATE;
}
// Initialize the RejectDetail argument.
*RejectDetail = 0;
// Wait for I/O completion. This sample assumes that the I/O operation was
// started using the code example shown in the SensorAdapterStartCapture
// documentation.
SetLastError(ERROR_SUCCESS);
result = GetOverlappedResult(
Pipeline->SensorHandle,
&sensorContext->Overlapped,
&bytesReturned,
TRUE
);
if (!result)
{
// There was an I/O error.
return _AdapterGetHresultFromWin32(GetLastError());
}
if (bytesReturned == sizeof(DWORD))
{
// The buffer is not large enough. This can happen if a device needs a
// bigger buffer depending on the purpose. Allocate a larger buffer and
// force the caller to reissue their I/O request.
DWORD allocationSize = sensorContext->CaptureBuffer->PayloadSize;
// Allocate at least the minimum buffer size needed to retrieve the
// payload structure.
if (allocationSize < sizeof(WINBIO_CAPTURE_DATA))
{
allocationSize = sizeof(WINBIO_CAPTURE_DATA);
}
// Free the old buffer and allocate a new one.
_AdapterRelease(sensorContext->CaptureBuffer);
sensorContext->CaptureBuffer = NULL;
sensorContext->CaptureBuffer =
(PWINBIO_CAPTURE_DATA)_AdapterAlloc(allocationSize);
if (sensorContext->CaptureBuffer == NULL)
{
sensorContext->CaptureBufferSize = 0;
return E_OUTOFMEMORY;
}
sensorContext->CaptureBufferSize = allocationSize;
return WINBIO_E_BAD_CAPTURE;
}
// Normalize the status value before sending it back to the biometric service.
if (sensorContext->CaptureBuffer != NULL &&
sensorContext->CaptureBufferSize >= sizeof(WINBIO_CAPTURE_DATA))
{
switch (sensorContext->CaptureBuffer->SensorStatus)
{
case WINBIO_SENSOR_ACCEPT:
{
// The capture was acceptable.
DWORD cbRead = sensorContext->CaptureBuffer->CaptureData.Size;
UCHAR * data = sensorContext->CaptureBuffer->CaptureData.Data;
//wprintf(L"%d ", cbRead);
break;
}
case WINBIO_SENSOR_REJECT:
// The capture was not acceptable. Overwrite the WinBioHresult value
// in case it has not been properly set.
sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_BAD_CAPTURE;
break;
case WINBIO_SENSOR_BUSY:
// The device is busy. Reset the WinBioHresult value in case it
// has not been properly set.
sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_DEVICE_BUSY;
break;
case WINBIO_SENSOR_READY:
case WINBIO_SENSOR_NOT_CALIBRATED:
case WINBIO_SENSOR_FAILURE:
default:
// There has been a device failure. Reset the WinBioHresult value
// in case it has not been properly set.
sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_INVALID_DEVICE_STATE;
break;
}
*RejectDetail = sensorContext->CaptureBuffer->RejectDetail;
hr = sensorContext->CaptureBuffer->WinBioHresult;
}
else
{
// The buffer is not large enough or the buffer pointer is NULL.
hr = WINBIO_E_INVALID_DEVICE_STATE;
}
return hr;
}
I used the next code from this github project
HRESULT CaptureSample()
{
HRESULT hr = S_OK;
WINBIO_SESSION_HANDLE sessionHandle = NULL;
WINBIO_UNIT_ID unitId = 0;
WINBIO_REJECT_DETAIL rejectDetail = 0;
PWINBIO_BIR sample = NULL;
SIZE_T sampleSize = 0;
// Connect to the system pool.
hr = WinBioOpenSession(
WINBIO_TYPE_FINGERPRINT, // Service provider
WINBIO_POOL_SYSTEM, // Pool type
WINBIO_FLAG_RAW, // Access: Capture raw data //To call WinBioCaptureSample function successfully, you must open the session handle by specifying WINBIO_FLAG_RAW
//WINBIO_FLAG_RAW: The client application captures raw biometric data using WinBioCaptureSample.
NULL, // Array of biometric unit IDs //NULL if the PoolType parameter is WINBIO_POOL_SYSTEM
0, // Count of biometric unit IDs//zero if the PoolType parameter is WINBIO_POOL_SYSTEM.
WINBIO_DB_DEFAULT, // Default database
&sessionHandle // [out] Session handle
);
if(FAILED(hr))
{
std::cout << "WinBioOpenSession failed. hr = 0x" << std::hex << hr << std::dec << "\n";
if(sample != NULL)
{
WinBioFree(sample);
sample = NULL;
}
if(sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return hr;
}
// Capture a biometric sample.
std::cout << "Calling WinBioCaptureSample - Swipe sensor...\n";
hr = WinBioCaptureSample(
sessionHandle,
WINBIO_NO_PURPOSE_AVAILABLE,
WINBIO_DATA_FLAG_RAW,//WINBIO_DATA_FLAG_RAW
&unitId,
&sample,
&sampleSize,
&rejectDetail
);
if(FAILED(hr))
{
if(hr == WINBIO_E_BAD_CAPTURE)
std:: cout << "Bad capture; reason: " << rejectDetail << "\n";
else
std::cout << "WinBioCaptureSample failed.hr = 0x" << std::hex << hr << std::dec << "\n";
if(sample != NULL)
{
WinBioFree(sample);
sample = NULL;
}
if(sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return hr;
}
std::cout << "Swipe processed - Unit ID: " << unitId << "\n";
std::cout << "Captured " << sampleSize << " bytes.\n";
if(sample != NULL)
{
WinBioFree(sample);
sample = NULL;
}
if(sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return hr;
}
int main()
{
EnumerateSensors();
CreateDirectoryA("data", NULL);
CaptureSample();
}
Sometimes my code is stuck in a loop and other times is not :(
Any hint is welcomed Thanks.
It seems I might have found a temporary solution to my problem.
First what I did is to change the sensor mode from basic to advanced.
[DriverPlugInAddReg]
HKR,WinBio\Configurations,DefaultConfiguration,,"0"
HKR,WinBio\Configurations\0,SensorMode,0x10001,2 ; Basic - 1, Advanced - 2
And the loop won't start
But I also noticed If I comment the call to the Sleep method inside CaptureSleepThread the loop starts again.
My guess is that Windows Biometric Service is expecting the method CaptureSleepThread to take some time to be considered successful but if the method ends very fast it is considered to be failed despite of the successful responses S_OK and WINBIO_SENSOR_ACCEPT and Windows Biometric Service will retry again calling to SensorAdapterStartCapture causing a loop.
DWORD WINAPI
CaptureSleepThread(
LPVOID lpParam
)
{
CBiometricDevice *device = (CBiometricDevice *) lpParam;
PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams();
//
// 1 minute or half a minute delay is the trick.
//
if (sleepParams->SleepValue > 60)
{
sleepParams->SleepValue = 60;
}
Sleep(sleepParams->SleepValue * 1000);
UCHAR szBuffer[] = { 0x08, 0x01, 0x00, 0x02 };
ULONG cbRead = 4;
sleepParams->captureData->WinBioHresult = S_OK;
sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
sleepParams->captureData->RejectDetail = 0;
sleepParams->captureData->CaptureData.Size = cbRead;
RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);
device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information);
return 0;
}
Other things I observed can cause a loop is when the call fails and not proper response is set.
//No delay is going to cause a loop
//Sleep(sleepParams->SleepValue * 1000);
//zeroes buffer is considered failed
UCHAR szBuffer[] = { 0x00, 0x00, 0x00, 0x00 };
//zero size is a failed read
ULONG cbRead = 0;
//returning other than S_FALSE or S_OK will cause a loop
sleepParams->captureData->WinBioHresult = S_FALSE;
sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
sleepParams->captureData->RejectDetail = 0;
sleepParams->captureData->CaptureData.Size = cbRead;
//It is going to fail if CaptureData.Data is empty
//RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);
Also attaching a debugger like WinDbg or Visual Studio will cause a loop so is better to debug using only Trace messages and TraceView tool and attach a debugger when necessary and a loop will be expected in this case, just ignore it.
I'm trying to split up an contiguous buffer into 3 byte channels (RGB). Here is my acutal workflow to get the buffer filled with an image:
Set up an Source Reader (MFVideoFormat_RGB32)
Receive video format information
Read first image and convert to contiguous buffer...
In addition to that, here is the code:
HRESULT hr = S_OK;
IMFAttributes *attributes = NULL;
SafeRelease(&_sourcereader);
hr = MFCreateAttributes(&attributes, 1);
if (FAILED(hr)) {
// TODO: set error
return false;
}
hr = attributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, true);
if (FAILED(hr)) {
// TODO: set error
return false;
}
// conversion from qstring to const wchar*
const WCHAR* wfilename = filename.toStdWString().c_str();
// create source reader from file with attributes
hr = MFCreateSourceReaderFromURL(wfilename, attributes, &_sourcereader);
if (FAILED(hr)) {
// TODO: set error
return false;
}
// configure sourcereader for progressive RGB32 frames
IMFMediaType *mediatype = NULL;
hr = MFCreateMediaType(&mediatype);
if (SUCCEEDED(hr))
{
hr = mediatype->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if (SUCCEEDED(hr))
{
hr = mediatype->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
}
if (SUCCEEDED(hr))
{
hr = _sourcereader->SetCurrentMediaType(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
NULL, mediatype);
}
// Ensure the stream is selected.
if (SUCCEEDED(hr))
{
hr = _sourcereader->SetStreamSelection(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE);
}
if (FAILED(hr)) {
// TODO: Error log for failed configuration
std::cout << "(ConfigureSourceReader) Configuration failed" << std::endl;
return false;
}
//------------------------------------------------------------------
//---------------------- Get Video Format Infos --------------------
//------------------------------------------------------------------
GUID subtype = { 0 };
// Get the media type from the stream.
hr = _sourcereader->GetCurrentMediaType(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &mediatype );
// Make sure it is a video format.
hr = mediatype->GetGUID(MF_MT_SUBTYPE, &subtype);
if (subtype != MFVideoFormat_RGB32)
{
hr = E_UNEXPECTED;
// TODO: Error log message
SafeRelease(&mediatype);
return false;
}
//------------------------------------------------------------------
// Get the width and height
UINT32 width = 0, height = 0;
hr = MFGetAttributeSize(mediatype, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr))
{
// TODO: Error log message
SafeRelease(&mediatype);
return false;
}
//assign dimensions to VideoInfo
_videoinfo.imageHeight = height; _videoinfo.imageWidth = width;
//std::cout << "(GetVideoFormat) width: " << width << ", height: " << height << std::endl;
//------------------------------------------------------------------
//get framerate
UINT32 framerate_num = 0, framerate_denom = 0;
hr = MFGetAttributeRatio(mediatype, MF_MT_FRAME_RATE, &framerate_num, &framerate_denom);
if (FAILED(hr))
{
// TODO: Error log message
SafeRelease(&mediatype);
return false;
}
//set frame rate in struct
_videoinfo.fps = framerate_num / framerate_denom; // TODO: check for valid fps 24,25,30 ...
//------------------------------------------------------------------
// Get length
LONGLONG length = 0;
PROPVARIANT var;
PropVariantInit(&var);
hr = _sourcereader->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE,
MF_PD_DURATION,
&var
);
if (SUCCEEDED(hr)) {
assert(var.vt == VT_UI8);
length = var.hVal.QuadPart;
} else {
// TODO : erro log msg
return false;
}
//Get total framenumber and length: save to info struct
_videoinfo.noofFrames = length / 10000000 * this->getFrameRate(); // incl. conversion from nano sec to sec
_videoinfo.duration = length;
//------------------------------------------------------------------
// Get the stride to find out if the bitmap is top-down or bottom-up.
LONG lStride = 0;
lStride = (LONG)MFGetAttributeUINT32(mediatype, MF_MT_DEFAULT_STRIDE, 1);
_videoinfo.stride = lStride;
_videoinfo.bTopDown = (lStride > 0);
//------------------------------------------------------------------
SafeRelease(&mediatype);
// return true and flag if initialization went well
_bInitialized = true;
return true;
After that I call a function to read a single frame (at the moment the first one).
HRESULT hr = S_OK;
IMFSample *pSample = NULL;
IMFMediaBuffer *buffer = NULL;
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
// Read Sample (RGB32)
hr = _sourcereader->ReadSample (
(DWORD) MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
&streamIndex,
&flags,
&llTimeStamp,
&pSample);
if (FAILED (hr)) {
// TODO handle fail case
}
//convert sample data to buffer
hr = pSample->ConvertToContiguousBuffer(&buffer);
if (FAILED (hr)) {
// TODO handle fail case
}
I know that by calling the function buffer->Lock(&pixels, NULL, &nPixels) that I can get the BYTE-stream stored in pixels. In my case I create a custom image with the given height and width (from SourceReader; [first function]). From the empty image I can get an empty color matrix which has to be filled with the following funtion: Color (byte red, byte green, byte blue)
I dont know how to split my RGB32 BYTE array into the single channels to fill my image? Maybe it is a silly question but I am relatively new to this area...
For RGB32 the byte format is:
R=Red
G=Green
B=Blue
A=Transparency
RGBARGBARGBA...
A very simple pseudo-code example of extracting the channels is shown below.
for (int row = 0; row < height; row++) {
for (int col = 0; col < stride; col += 4) {
redBuf[rIndex++] = sample[row * stride + col];
greenBuf[gIndex++] = sample[row * stride + col + 1];
blueBuf[bIndex++] = sample[row * stride + col + 2];
transparencyBuf[tIndex++] = sample[row * stride + col + 3];
}
}
My main goal is to capture 2 audio streams and store them as a vector<BYTE> then come up with a congruence algorithm to check for equality. Right now I am only capturing one stream, however the values of the stream are 0 '/0'. Why am I getting null terminated values for all elements in my BYTE vector?
void AudioDeviceOperator::TakeInput(AudioStreamModel* m)
{
HRESULT hr;
IAudioClient *iac = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
WAVEFORMATEX *mixFormat;
UINT32 bufferFrameCount;
HRESULT de;
de = AudioDeviceEnumerator -> GetDefaultAudioEndpoint(eCapture, eConsole, &SelectedAudioDeviceModel->AudioDevice);
hr = SelectedAudioDeviceModel->AudioDevice -> Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&iac);
REFERENCE_TIME bufferDuration = 0; //default to min
REFERENCE_TIME periodicity = 0;
GUID trashGuid;
HRESULT tg = CoCreateGuid(&trashGuid);
LPCGUID AudioSessionGuid = &trashGuid;
GUID guid2 = *AudioSessionGuid;
HRESULT guidError = UuidCreate(&guid2); //project -> properties -> Linker -> Command Line -> Rpctr4.lib
iac->GetMixFormat(&mixFormat);
m->StreamFormat = *mixFormat;
if (SUCCEEDED(guidError)) {
cout << "/n" << "Initializing audio stream..";
hr = iac->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_CROSSPROCESS, bufferDuration, periodicity, mixFormat, AudioSessionGuid);
cout << hr;
hr = iac->GetBufferSize(&bufferFrameCount);
cout << hr;
iac->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient);
// Calculate the actual duration of the allocated buffer.
double hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / mixFormat-> nSamplesPerSec;
bool recordAudio = TRUE;
BYTE *sData;
UINT32 numFramesAvailable = 0;
DWORD flags;
UINT32 packetLength = 0;
int numOfPackets = 0;
iac->Start();
while (recordAudio == TRUE)
{
hr = pCaptureClient->GetNextPacketSize(&packetLength);
while (packetLength != 0) {
hr = pCaptureClient->GetBuffer(&sData, &numFramesAvailable, &flags, NULL, NULL);
if (sData != NULL) {
m->Stream.push_back((*sData)); //here is where I write to the vector
numOfPackets++;
}
if (numOfPackets == 100) { // just getting 100 packets for testing
recordAudio = FALSE;
break;
}
}
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
}
}
else
cout << "AudioSessionGuidError";
CoTaskMemFree(iac);
AudioDeviceEnumerator->Release();
//pCaptureClient->Release(); // releaseBuffer seeming to release capture client interface as weell.
};
When the audio session starts I make sure to make some noise. With the vector values the way they are I have nothing to compare. I'm also assuming that using that byte vector and rendering it with a IAudioRenderClient will result in nothing, however that is my next plan of action. Any ideas??
We are currently using the NetBios method, and it works ok under XP. Preliminary tests under Vista show that it also works, but there are caveats - NetBIOS has to be present, for instance, and from what I've been reading, the order of the adapters is bound to change. Our alternative method - with SNMPExtensionQuery - seems to be broken under Vista.
The question is: do you know of a reliable way to get a list of the local MAC addresses on a Vista machine? Backwards compatibility with XP is a plus (I'd rather have one single method than lots of ugly #ifdef's). Thanks!
This will give you a list of all MAC addresses on your computer. It will work with all versions of Windows as well:
void getdMacAddresses(std::vector<std::string> &vMacAddresses;)
{
vMacAddresses.clear();
IP_ADAPTER_INFO AdapterInfo[32]; // Allocate information for up to 32 NICs
DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
DWORD dwStatus = GetAdaptersInfo( // Call GetAdapterInfo
AdapterInfo, // [out] buffer to receive data
&dwBufLen); // [in] size of receive data buffer
//No network card? Other error?
if(dwStatus != ERROR_SUCCESS)
return;
PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
char szBuffer[512];
while(pAdapterInfo)
{
if(pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
{
sprintf_s(szBuffer, sizeof(szBuffer), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
, pAdapterInfo->Address[0]
, pAdapterInfo->Address[1]
, pAdapterInfo->Address[2]
, pAdapterInfo->Address[3]
, pAdapterInfo->Address[4]
, pAdapterInfo->Address[5]
);
vMacAddresses.push_back(szBuffer);
}
pAdapterInfo = pAdapterInfo->Next;
}
}
Could you use the WMIService? I used it to get the mac-address of a machine in pre-Vista days though.
Old question, already answered, but this is safer code - in case WMI can't be fully initialized.
For getting access to information about your system, here is a minimalist class that tries to stay safe:
NOTE: ToString, convertToUtf8 and convertFromUtf8 are left as an exercise for the reader. :)
NOTE: I've just shown the safe initialization and tear-down of the WMI system, and the basics of getting values from WMI, and getting the MAC Addresses (the question in the OP).
This came from working code, but was modified as I pasted it in here. So it is possible other things got left out that ought to have been included. Oops.
class WmiAccessor
{
public:
WmiAccessor()
: _pWbemLocator(NULL)
, _pWbemServices(NULL)
, _com_initialized(false)
, _com_need_uninitialize(false)
, _svc_initialized(false)
, _loc_initialized(false)
, _all_initialized(false)
, _errors("")
, m_mutex()
{
HRESULT hr;
hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
switch (hr)
{
case S_OK:
// The COM library was initialized successfully on this thread.
_com_initialized = true;
_com_need_uninitialize = true;
break;
case S_FALSE:
// The COM library is already initialized on this thread.
_com_initialized = true;
_com_need_uninitialize = true;
break;
case RPC_E_CHANGED_MODE:
// A previous call to CoInitializeEx specified the concurrency model
// for this thread as multithread apartment (MTA).
// This could also indicate that a change from neutral-threaded apartment to
// single-threaded apartment has occurred.
_com_initialized = true;
_com_need_uninitialize = false;
break;
default:
_com_initialized = false;
_com_need_uninitialize = false;
_errors += "Failed to initialize COM.\r\n";
return;
}
hr = ::CoInitializeSecurity(NULL, -1, NULL, NULL,
0 /*RPC_C_AUTHN_LEVEL_DEFAULT*/,
3 /*RPC_C_IMP_LEVEL_IMPERSONATE*/,
NULL, EOAC_NONE, NULL);
// RPC_E_TOO_LATE == Security must be initialized before!
// It cannot be changed once initialized. I don't care!
if (FAILED(hr) && (hr != RPC_E_TOO_LATE))
{
_errors += "Failed to initialize COM Security.\r\n";
if (_com_need_uninitialize)
{
::CoUninitialize();
_com_need_uninitialize = false;
}
return;
}
hr = _pWbemLocator.CoCreateInstance(CLSID_WbemLocator);
if (FAILED(hr) || (_pWbemLocator == nullptr))
{
_errors += "Failed to initialize WBEM Locator.\r\n";
return;
}
_loc_initialized = true;
hr = _pWbemLocator->ConnectServer(
CComBSTR(L"root\\cimv2"), NULL, NULL, 0, NULL, 0, NULL, &_pWbemServices);
if (FAILED(hr) || (_pWbemServices == nullptr))
{
_errors += "Failed to connect WBEM Locator.\r\n";
_pWbemLocator.Release();
_loc_initialized = false;
return;
}
else
{
_svc_initialized = true;
// Set security Levels on the proxy
hr = CoSetProxyBlanket(_pWbemServices,
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hr))
{
_errors += "Failed to set proxy blanket.\r\n";
return;
}
}
_all_initialized = true;
}
~WmiAccessor()
{
std::unique_lock<std::mutex> slock(m_mutex);
if (_svc_initialized)
{
if (_pWbemServices)
_pWbemServices.Release();
_svc_initialized = false;
}
if (_loc_initialized)
{
if (_pWbemLocator)
_pWbemLocator.Release();
_loc_initialized = false;
}
if (_com_initialized)
{
if (_com_need_uninitialize)
{
::CoUninitialize();
}
_com_initialized = false;
_com_need_uninitialize = false;
}
_all_initialized = false;
}
// public: must lock
std::string get_and_clear_errors()
{
std::string result = "";
std::unique_lock<std::mutex> slock(m_mutex);
std::swap(result, _errors);
return result;
}
// public: must lock
std::string get_string(const std::string& name, const std::string& dflt /*= ""*/)
{
std::unique_lock<std::mutex> slock(m_mutex);
return _all_initialized ? _string(name) : dflt;
}
// public: must lock
uint32_t get_uint32(const std::string& name, uint32_t dflt /*= 0*/)
{
std::unique_lock<std::mutex> slock(m_mutex);
return _all_initialized ? _uint32(name) : dflt;
}
// similarly for other public accessors of basic types.
private:
CComPtr<IWbemLocator> _pWbemLocator;
CComPtr<IWbemServices> _pWbemServices;
volatile bool _com_initialized;
volatile bool _com_need_uninitialize;
volatile bool _svc_initialized;
volatile bool _loc_initialized;
volatile bool _all_initialized;
std::string _errors;
CComVariant _variant(const std::wstring& name);
std::string _string(const std::string& name);
uint32_t _uint32(const std::string& name);
uint16_t _uint16(const std::string& name);
uint8_t _uint8(const std::string& name);
std::vector<std::string> _macAddresses(bool forceReCalculate = false);
// to protect internal objects, public methods need to protect the internals.
//
mutable std::mutex m_mutex;
std::vector<std::string> _macs; // unlikely to change, so save them once found.
// internal: assumes inside a lock
CComVariant _variant(const std::wstring& name)
{
if (!_all_initialized)
return CComVariant();
CComPtr<IEnumWbemClassObject> pEnum;
CComBSTR cbsQuery = std::wstring(L"Select " + name + L" from Win32_OperatingSystem").c_str();
HRESULT hr = _pWbemServices->ExecQuery(
CComBSTR(L"WQL"), cbsQuery, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
CComVariant cvtValue;
if (FAILED(hr) || !pEnum)
{
std::wstring wquery(cbsQuery, SysStringLen(cbsQuery));
_errors += "Failed to exec WMI query: '" + convertToUtf8(wquery) + "'\r\n";
return cvtValue;
}
ULONG uObjectCount = 0;
CComPtr<IWbemClassObject> pWmiObject;
hr = pEnum->Next(WBEM_INFINITE, 1, &pWmiObject, &uObjectCount);
if (FAILED(hr) || !pWmiObject)
{
_errors
+= "Failed to get WMI Next result for: '" + convertToUtf8(name) + "'\r\n";
return cvtValue;
}
hr = pWmiObject->Get(name.c_str(), 0, &cvtValue, 0, 0);
if (FAILED(hr))
{
_errors
+= "Failed to get WMI result value for: '" + convertToUtf8(name) + "'\r\n";
}
return cvtValue;
}
// internal: assumes inside a lock
std::string _string(const std::string& name)
{
if (!_all_initialized)
return "";
CComVariant cvtValue = _variant(convertFromUtf8(name).c_str());
std::wstring wValue(cvtValue.bstrVal, SysStringLen(cvtValue.bstrVal));
std::string sValue = convertToUtf8(wValue);
return sValue;
}
// internal: assumes inside a lock
uint32_t _uint32(const std::string& name)
{
if (!_all_initialized)
return 0;
CComVariant cvtValue = _variant(convertFromUtf8(name).c_str());
uint32_t uValue = static_cast<uint32_t>(cvtValue.lVal);
return uValue;
}
// similarly for other internal access of basic types.
// internal: assumes inside a lock
std::vector<std::string> _macAddresses(bool forceReCalculate /*= false*/)
{
if (!_all_initialized)
{
return _macs; // it will still be empty at this point.
}
if (forceReCalculate)
{
_macs.clear();
}
if (_macs.empty())
{
// hr == 0x80041010 == WBEM_E_INVALID_CLASS
// hr == 0x80041017 == WBEM_E_INVALID_QUERY
// hr == 0x80041018 == WBEM_E_INVALID_QUERY_TYPE
CComBSTR cbsQuery = std::wstring(L"Select * from Win32_NetworkAdapter").c_str();
CComPtr<IEnumWbemClassObject> pEnum;
HRESULT hr = _pWbemServices->ExecQuery(
CComBSTR(L"WQL"), cbsQuery, WBEM_RETURN_IMMEDIATELY, NULL, &pEnum);
if (FAILED(hr))
{
_errors += "error: MacAddresses: ExecQuery('"
+ convertToUtf8((LPWSTR)cbsQuery) + "') returned "
+ ToString(hr) + "\r\n";
}
if (SUCCEEDED(hr))
{
ULONG fetched;
VARIANT var;
IWbemClassObject* pclsObj = NULL;
while (pEnum)
{
hr = pEnum->Next(WBEM_INFINITE, 1, &pclsObj, &fetched);
if (0 == fetched)
break;
std::string theMac = "";
VariantInit(&var);
hr = pclsObj->Get(L"MACAddress", 0, &var, 0, 0);
if (SUCCEEDED(hr))
{
switch (var.vt)
{
case VT_NULL: break;
case VT_BSTR:
theMac = (var.bstrVal == NULL)
? ""
: convertToUtf8(var.bstrVal);
break;
case VT_LPSTR:
theMac = (var.bstrVal == NULL)
? ""
: (const char*)var.bstrVal;
break;
case VT_LPWSTR:
theMac = (var.bstrVal == NULL)
? ""
: convertToUtf8((LPWSTR)var.bstrVal);
break;
// _could_ be array of BSTR, LPSTR, LPWSTR; unlikely, but ....
case VT_ARRAY | VT_BSTR:
case VT_ARRAY | VT_LPSTR:
case VT_ARRAY | VT_LPWSTR:
_errors += "warning: MacAddresses: unexpected array of addresses";
_errors += "\r\n";
// yet another exercise for the reader :)
break;
default:
_errors += "error: MacAddresses: unexpected VARIANT.vt = "
+ ToString(var.vt) + "\r\n";
break;
}
// local loopback has an empty address?
if (!theMac.empty())
{
_macs.push_back(theMac);
}
}
VariantClear(&var);
pclsObj->Release();
}
}
}
return _macs;
}
...
}
GetAdaptersInfo() is the official method, it enumerates all adapters even ones that are disconnected.
See this post for example code codeguru
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <vector>
#include <Windows.h>
#include <Iphlpapi.h>
#include <Assert.h>
#include <string>
#pragma comment(lib, "iphlpapi.lib")
char* getdMacAddresses()
{
IP_ADAPTER_INFO AdapterInfo[32]; // Allocate information for up to 32 NICs
DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
DWORD dwStatus = GetAdaptersInfo( // Call GetAdapterInfo
AdapterInfo, // [out] buffer to receive data
&dwBufLen); // [in] size of receive data buffer
//Exit When Error
if (dwStatus != ERROR_SUCCESS)
return "ERROR";
PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
char szBuffer[512];
while (pAdapterInfo)
{
if (pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
{
sprintf_s(szBuffer, sizeof(szBuffer), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
, pAdapterInfo->Address[0]
, pAdapterInfo->Address[1]
, pAdapterInfo->Address[2]
, pAdapterInfo->Address[3]
, pAdapterInfo->Address[4]
, pAdapterInfo->Address[5]
);
return szBuffer;
}
pAdapterInfo = pAdapterInfo->Next;
}
return "ERROR";
}
You can use WMI on both XP and Vista, there are a number of examples online. e.g:
Use Windows Management Instrumentation (WMI) to get a MAC Address