I just started getting into DirectInput today, using DirectInput8 with MinGW on Windows 7 Ultimate N. I started off with a simple program to report which keys are currently down every second (just codes, not readable keys). However, I can't even get as far as acquiring the keyboard before it errors out:
#define _WIN32_WINNT 0x0601
#include <dinput.h>
//link to dinput8.lib and dxguid.lib
int main() {
IDirectInput8 *dinput;
DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
IDirectInputDevice8 *kb;
dinput->CreateDevice(GUID_SysKeyboard, &kb, nullptr);
kb->SetDataFormat(&c_dfDIKeyboard);
kb->SetCooperativeLevel(GetConsoleWindow(), DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
kb->Acquire(); //fails with DIERR_INVALIDPARAM
}
I omitted error checking, but what happens is that every call succeeds (by judgement of the FAILED macro) up to Acquire(). That call fails with the error DIERR_INVALIDPARAM. I looked on the MSDN pages and across the web, but I can't find any reason it would fail with that based on everything before it present and working.
For good measure, I also tried looping the Acquire() call until it succeeded and then played around with windows and the keyboard while it was running, but the program never successfully acquired the keyboard in all the time it was running. How can I successfully acquire the keyboard?
Experimenting with direct input in a console program for the keyboard and joystick
It is not necessary to have a handle to a window to use direct input. You can just use null in the parameter for a handle to a window.
Instead of:
kb->SetCooperativeLevel(GetConsoleWindow(), DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
Use:
kb->SetCooperativeLevel(NULL, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
Here is an example program that uses direct input to display the keys 'a' and 'd' when those keys are pressed. It will repeat those keys when they are held for 300 milliseconds. The example program is also a good example of a high resolution counter. There is no need to add in properties in Visual Studio to add the libraries dinput8.lib and dxguid.lib. The #pragma comment code lines at the top of the program do that for you.
#include <dinput.h>
#include <iostream>
#include <Windows.h>
#pragma comment(lib,"dinput8.lib")
#pragma comment(lib,"dxguid.lib")
using std::cout;
using std::endl;
LPDIRECTINPUT8 di;
LPDIRECTINPUTDEVICE8 keyboard;
class CTimer
{
public:
CTimer() {
QueryPerformanceFrequency(&mqFreq);
}
~CTimer() {}
void Start() {
QueryPerformanceCounter(&mqStart);
}
void End() {
QueryPerformanceCounter(&mqEnd);
}
double GetTimeInSeconds() {
return (mqEnd.QuadPart - mqStart.QuadPart)/(double)mqFreq.QuadPart;
}
double GetTimeInMilliseconds() {
return (1.0e3*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
}
double GetTimeInMicroseconds() {
return (1.0e6*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
}
double GetTimeInNanoseconds() {
return (1.0e9*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
}
private:
LARGE_INTEGER mqStart;
LARGE_INTEGER mqEnd;
LARGE_INTEGER mqFreq;
};
HRESULT initializedirectinput8() {
HRESULT hr;
// Create a DirectInput device
if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, (VOID**)&di, NULL))) {
return hr;
}
}
void createdikeyboard() {
di->CreateDevice(GUID_SysKeyboard, &keyboard, NULL);
keyboard->SetDataFormat(&c_dfDIKeyboard);
keyboard->SetCooperativeLevel(NULL,DISCL_FOREGROUND | DISCL_EXCLUSIVE);
keyboard->Acquire();
}
void destroydikeyboard() {
keyboard->Unacquire();
keyboard->Release();
}
#define keydown(name,key) (name[key] & 0x80)
void main() {
HRESULT hr;
BYTE dikeys[256];
CTimer timeint;
CTimer timekeywaspressedclass;
double gettime;
double lastkeywasa=false;
double timekeywaspressed=0;
bool lasttimetherewasakeypress=false;
bool akeywaspressed=false;
initializedirectinput8();
createdikeyboard();
hr=keyboard->GetDeviceState(256,dikeys);
timeint.Start();
while (!keydown(dikeys,DIK_ESCAPE)) {
timeint.End();
gettime=timeint.GetTimeInMilliseconds();
if (gettime >=5) {
hr=keyboard->GetDeviceState(256,dikeys);
akeywaspressed=false;
if (keydown(dikeys,DIK_A)) {
akeywaspressed=true;
if (timekeywaspressed >=300) {
cout << "a";
lasttimetherewasakeypress=false;
}
}
if (keydown(dikeys,DIK_D)) {
akeywaspressed=true;
if (timekeywaspressed >=300) {
cout << "d";
lasttimetherewasakeypress=false;
}
}
if (lasttimetherewasakeypress==false && akeywaspressed==true) {
timekeywaspressedclass.Start();
timekeywaspressed=0;
lasttimetherewasakeypress=true;
}
if (lasttimetherewasakeypress==true && akeywaspressed==true) {
timekeywaspressedclass.End();
gettime=timekeywaspressedclass.GetTimeInMilliseconds();
timekeywaspressed+=gettime;
timekeywaspressedclass.Start();
}
if (akeywaspressed==false) {
lasttimetherewasakeypress=false;
timekeywaspressed=0;
}
} // end if (gettime >=5)
} // end while
destroydikeyboard();
} // end main
Here is an example console program for the joystick.
#include <dinput.h>
#include <iostream>
#include <Windows.h>
#pragma comment(lib,"dinput8.lib")
#pragma comment(lib,"dxguid.lib")
using std::cout;
using std::endl;
LPDIRECTINPUT8 di;
LPDIRECTINPUTDEVICE8 joystick;
LPDIRECTINPUTDEVICE8 keyboard;
DIJOYSTATE2 js;
BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context);
BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context);
HRESULT selectjoystick() {
HRESULT hr;
// Look for the first simple joystick we can find.
if (FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback,
NULL, DIEDFL_ATTACHEDONLY))) {
return hr;
}
// Make sure we got a joystick
if (joystick == NULL) {
// printf("Joystick not found.\n");
return E_FAIL;
}
}
DIDEVCAPS capabilities;
HRESULT setjoystickproperties() {
HRESULT hr;
// Set the data format to "simple joystick" - a predefined data format
//
// A data format specifies which controls on a device we are interested in,
// and how they should be reported. This tells DInput that we will be
// passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState().
if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) {
return hr;
}
// Set the cooperative level to let DInput know how this device should
// interact with the system and with other DInput applications.
if (FAILED(hr = joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE |
DISCL_FOREGROUND))) {
return hr;
}
// Determine how many axis the joystick has (so we don't error out setting
// properties for unavailable axis)
capabilities.dwSize = sizeof(DIDEVCAPS);
if (FAILED(hr = joystick->GetCapabilities(&capabilities))) {
return hr;
}
}
BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{
HRESULT hr;
// Obtain an interface to the enumerated joystick.
hr = di->CreateDevice(instance->guidInstance, &joystick, NULL);
// If it failed, then we can't use this joystick. (Maybe the user unplugged
// it while we were in the middle of enumerating it.)
if (FAILED(hr)) {
return DIENUM_CONTINUE;
}
// Stop enumeration. Note: we're just taking the first joystick we get. You
// could store all the enumerated joysticks and let the user pick.
return DIENUM_STOP;
}
HRESULT enumaxes() {
HRESULT hr;
// Enumerate the axes of the joyctick and set the range of each axis. Note:
// we could just use the defaults, but we're just trying to show an example
// of enumerating device objects (axes, buttons, etc.).
if (FAILED(hr = joystick->EnumObjects(enumAxesCallback, NULL, DIDFT_AXIS))) {
return hr;
}
}
BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context)
{
HWND hDlg = (HWND)context;
DIPROPRANGE propRange;
propRange.diph.dwSize = sizeof(DIPROPRANGE);
propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
propRange.diph.dwHow = DIPH_BYID;
propRange.diph.dwObj = instance->dwType;
propRange.lMin = -1000;
propRange.lMax = +1000;
// Set the range for the axis
if (FAILED(joystick->SetProperty(DIPROP_RANGE, &propRange.diph))) {
return DIENUM_STOP;
}
return DIENUM_CONTINUE;
}
HRESULT polljoy() {
HRESULT hr;
if (joystick == NULL) {
return S_OK;
}
// Poll the device to read the current state
hr = joystick->Poll();
if (FAILED(hr)) {
// DInput is telling us that the input stream has been
// interrupted. We aren't tracking any state between polls, so
// we don't have any special reset that needs to be done. We
// just re-acquire and try again.
hr = joystick->Acquire();
while (hr == DIERR_INPUTLOST) {
hr = joystick->Acquire();
}
// If we encounter a fatal error, return failure.
if ((hr == DIERR_INVALIDPARAM) || (hr == DIERR_NOTINITIALIZED)) {
return E_FAIL;
}
// If another application has control of this device, return successfully.
// We'll just have to wait our turn to use the joystick.
if (hr == DIERR_OTHERAPPHASPRIO) {
return S_OK;
}
}
// Get the input's device state
if (FAILED(hr = joystick->GetDeviceState(sizeof(DIJOYSTATE2), &js))) {
return hr; // The device should have been acquired during the Poll()
}
return S_OK;
}
class CTimer
{
public:
CTimer() {
QueryPerformanceFrequency(&mqFreq);
}
~CTimer() {}
void Start() {
QueryPerformanceCounter(&mqStart);
}
void End() {
QueryPerformanceCounter(&mqEnd);
}
double GetTimeInSeconds() {
return (mqEnd.QuadPart - mqStart.QuadPart)/(double)mqFreq.QuadPart;
}
double GetTimeInMilliseconds() {
return (1.0e3*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
}
double GetTimeInMicroseconds() {
return (1.0e6*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
}
double GetTimeInNanoseconds() {
return (1.0e9*(mqEnd.QuadPart - mqStart.QuadPart))/mqFreq.QuadPart;
}
private:
LARGE_INTEGER mqStart;
LARGE_INTEGER mqEnd;
LARGE_INTEGER mqFreq;
};
HRESULT initializedirectinput8() {
HRESULT hr;
// Create a DirectInput device
if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, (VOID**)&di, NULL))) {
return hr;
}
}
void createdikeyboard() {
di->CreateDevice(GUID_SysKeyboard, &keyboard, NULL);
keyboard->SetDataFormat(&c_dfDIKeyboard);
keyboard->SetCooperativeLevel(NULL,DISCL_FOREGROUND | DISCL_EXCLUSIVE);
keyboard->Acquire();
}
void destroydikeyboard() {
keyboard->Unacquire();
keyboard->Release();
}
void closejoystick() {
if (joystick) {
joystick->Unacquire();
}
if (joystick) {
joystick->Release();
}
}
#define keydown(name,key) (name[key] & 0x80)
void main() {
HRESULT hr;
CTimer pollintclass;
hr=initializedirectinput8();
createdikeyboard();
hr=selectjoystick();
hr=setjoystickproperties();
hr=enumaxes();
bool endloop=false;
double gettime;
BYTE dikeys[256];
pollintclass.Start();
while (endloop==false) {
pollintclass.End();
gettime=pollintclass.GetTimeInMilliseconds();
if (gettime >=5) {
hr=keyboard->GetDeviceState(256,dikeys);
if (FAILED(hr))
keyboard->Acquire();
if (keydown(dikeys,DIK_ESCAPE))
endloop=true;
polljoy();
cout << "joy x-axis=" << js.lX << " " << "joy y-axis=" << js.lY << endl;
pollintclass.Start();
}
}
destroydikeyboard();
closejoystick();
}
Have you tried DISCL_BACKGROUND instead of DISCL_FOREGROUND ?
It actually happens that the error magically disappears when I change it from associating with the console window (which is visibly existent) to one I create in the SetCooperativeLevel() call. Why a console window can't be used I don't know, so I'll leave this unaccepted for a while in lieu of an answerer who does, but this does solve the problem.
Related
I need help in reading data from a specific joystick when it is connected to a PC in which the data read is received by 2019 visual studio c++ pc program. I want it to detect a particular joystick when connected to a PC based on a product ID or something similar, I don't want it just to detect a joystick that's connected. Then, I would obviously like to have it be read from it and display the read data on the console using cout or something similar. Please provide me a complete example as I've looked at many APIs and function prototypes of the DirectX library to use and I'm still very lost. Thanks. Below is what I have so far;
#include <iostream>
using namespace std;
#include <Windows.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
LPDIRECTINPUT8 di;
HRESULT hr;
LPDIRECTINPUTDEVICE8 joystick;
OLECHAR* ap;
DIDEVCAPS capabilities;
BOOL CALLBACK
enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{
HRESULT hr;
// Obtain an interface to the enumerated joystick.
hr = di->CreateDevice(instance->guidInstance, &joystick, NULL);
// If it failed, then we can't use this joystick. (Maybe the user unplugged
// it while we were in the middle of enumerating it.)
if (FAILED(hr)) {
return DIENUM_CONTINUE;
}
// Stop enumeration. Note: we're just taking the first joystick we get. You
// could store all the enumerated joysticks and let the user pick.
return DIENUM_STOP;
}
BOOL CALLBACK
enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context) {
HWND hDlg = (HWND)context;
DIPROPRANGE propRange;
propRange.diph.dwSize = sizeof(DIPROPRANGE);
propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
propRange.diph.dwHow = DIPH_BYID;
propRange.diph.dwObj = instance->dwType;
propRange.lMin = -1000;
propRange.lMax = +1000;
if (FAILED(joystick->SetProperty(DIPROP_RANGE, &propRange.diph))) return DIENUM_STOP;
return DIENUM_CONTINUE;
}
HRESULT
poll(DIJOYSTATE2* js) {
HRESULT hr;
if (joystick == NULL) {
return S_OK;
}
hr = joystick->Poll();
if (FAILED(hr)) {
hr = joystick->Acquire();
while (hr == DIERR_INPUTLOST) hr = joystick->Acquire();
if ((hr == DIERR_INVALIDPARAM) || (hr == DIERR_NOTINITIALIZED)) return E_FAIL;
if (hr == DIERR_OTHERAPPHASPRIO) return S_OK;
}
if (FAILED(hr = joystick->GetDeviceState(sizeof(DIJOYSTATE2), js))) return hr;
return S_OK;
}
int main() {
// Create a DirectInput device
if(FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&di, NULL))) return hr;
if(FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback, NULL, DIEDFL_ATTACHEDONLY))) return hr;
// Make sure we got a joystick
if (joystick == NULL){
printf("Joystick not found.\n");
return E_FAIL;
}
if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) return hr;
if (FAILED(hr = joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE | DISCL_FOREGROUND))) return hr;
capabilities.dwSize = sizeof(DIDEVCAPS);
if (FAILED(hr = joystick->GetCapabilities(&capabilities))) return hr;
if (FAILED(hr = joystick->EnumObjects(enumAxesCallback, NULL, DIDFT_AXIS))) return hr;
if (joystick) {
joystick->Unacquire();
}
return 0;
}
The Joysticks works as expected except that if it's not plugged my app crashes. The interesting thing, I used it on the computer that I make the development in, it does not crash when not plugged, only on other computers. So that's something to keep in mind
The way I used the API is as follow :
I setup global variables
LPDIRECTINPUTDEVICE8 JoystickDriver::joystick;
LPDIRECTINPUT8 JoystickDriver::di;
DIDEVCAPS JoystickDriver::capabilities;
HRESULT JoystickDriver::hr;
DIJOYSTATE2 JoystickDriver::jstate;
In the callback function that is executed at a fixed time step
Main Callback :
if (*isInitialized != 1 )
{
js->initialize();
js->selectJoystick();
js->setProperties();
js->enumAxes();
*isInitialized = 1;
}
if (js->PollData(&Joystick::jstate) == S_OK)
{
slider[0] = (real_T)(1000 - Joystick::jstate.rglSlider[0]) / 20;
pov[0] = (real_T)Joystick::jstate.lX;
pov[1] = (real_T)Joystick::jstate.lY;
pov[2] = (real_T)Joystick::jstate.lRz;
Buttons[0] = (uint8_T)(Joystick::jstate.rgbButtons[0] & 0x80);
Buttons[1] = (uint8_T)(Joystick::jstate.rgbButtons[1] & 0x80);
Buttons[2] = (uint8_T)(Joystick::jstate.rgbButtons[2] & 0x80);
Buttons[3] = (uint8_T)(Joystick::jstate.rgbButtons[3] & 0x80);
Buttons[4] = (uint8_T)(Joystick::jstate.rgbButtons[4] & 0x80);
}
In the Class definition/implementation
Joystick.h
class Joystick
{
public:
Joystick();
~Joystick();
HRESULT initialize();
HRESULT selectJoystick();
static BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* context);
HRESULT setProperties();
HRESULT enumAxes();
static BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context);
HRESULT PollData(DIJOYSTATE2 *js);
void disConnectJoystick();
static LPDIRECTINPUTDEVICE8 joystick;
static LPDIRECTINPUT8 di;
static DIDEVCAPS capabilities;
static HRESULT hr;
static DIJOYSTATE2 jstate;
};
Joystick.cpp
#include "Joystick.h"
Joystick::Joystick()
{
}
Joystick::~Joystick()
{
}
HRESULT Joystick::initialize()
{
// Create a DirectInput device
if (FAILED(Joystick::hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, (VOID**)&(Joystick::di), NULL)))
{
return Joystick::hr;
}
else
return E_FAIL;
}
HRESULT Joystick::selectJoystick()
{
// Look for the first simple joystick we can find.
if (FAILED(Joystick::hr = this->di->EnumDevices(DI8DEVCLASS_GAMECTRL, &Joystick::enumCallback, NULL, DIEDFL_ATTACHEDONLY)))
{
return Joystick::hr;
}
// Make sure we got a joystick
if (Joystick::joystick == NULL) {
printf("Joystick not found.\n");
return E_FAIL;
}
return Joystick::hr;
}
BOOL CALLBACK Joystick::enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{
// Obtain an interface to the enumerated joystick.
Joystick::hr = (Joystick::di)->CreateDevice(instance->guidInstance, &Joystick::joystick, NULL);
// If it failed, then we can't use this joystick. (Maybe the user unplugged
// it while we were in the middle of enumerating it.)
if (FAILED(Joystick::hr)) {
return DIENUM_CONTINUE;
}
// Stop enumeration. Note: we're just taking the first joystick we get. You
// could store all the enumerated joysticks and let the user pick.
return DIENUM_STOP;
}
HRESULT Joystick::setProperties()
{
if (FAILED(Joystick::hr = Joystick::joystick->SetDataFormat(&c_dfDIJoystick2)))
{
return Joystick::hr;
}
// Set the cooperative level to let DInput know how this device should
// interact with the system and with other DInput applications.
if (FAILED(Joystick::hr = Joystick::joystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE | DISCL_FOREGROUND)))
{
return Joystick::hr;
}
// Determine how many axis the joystick has (so we don't error out setting
// properties for unavailable axis)
Joystick::capabilities.dwSize = sizeof(DIDEVCAPS);
if (FAILED(Joystick::hr = Joystick::joystick->GetCapabilities(&Joystick::capabilities)))
{
return Joystick::hr;
}
return E_FAIL;
}
HRESULT Joystick::enumAxes()
{
if (FAILED(Joystick::hr = Joystick::joystick->EnumObjects(Joystick::enumAxesCallback, NULL, DIDFT_AXIS)))
{
return Joystick::hr;
}
else
return Joystick::hr;
}
BOOL CALLBACK Joystick::enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context)
{
HWND hDlg = (HWND)context;
DIPROPRANGE propRange;
propRange.diph.dwSize = sizeof(DIPROPRANGE);
propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
propRange.diph.dwHow = DIPH_BYID;
propRange.diph.dwObj = instance->dwType;
propRange.lMin = -1000;
propRange.lMax = +1000;
// Set the range for the axis
if (FAILED(Joystick::joystick->SetProperty(DIPROP_RANGE, &propRange.diph)))
{
return DIENUM_STOP;
}
return DIENUM_CONTINUE;
}
HRESULT Joystick::PollData(DIJOYSTATE2 *js)
{
if (Joystick::joystick == NULL) {
return S_OK;
}
// Poll the device to read the current state
Joystick::hr = Joystick::joystick->Poll();
if (FAILED(Joystick::hr))
{
// DInput is telling us that the input stream has been
// interrupted. We aren't tracking any state between polls, so
// we don't have any special reset that needs to be done. We
// just re-acquire and try again.
Joystick::hr = Joystick::joystick->Acquire();
while (Joystick::hr == DIERR_INPUTLOST)
{
Joystick::hr = Joystick::joystick->Acquire();
}
// If we encounter a fatal error, return failure.
if ((Joystick::hr == DIERR_INVALIDPARAM) || (Joystick::hr == DIERR_NOTINITIALIZED))
{
return E_FAIL;
}
// If another application has control of this device, return successfully.
// We'll just have to wait our turn to use the joystick.
if (Joystick::hr == DIERR_OTHERAPPHASPRIO)
{
return S_OK;
}
}
// Get the input's device state
if (FAILED(Joystick::hr = Joystick::joystick->GetDeviceState(sizeof(DIJOYSTATE2), js)))
{
return Joystick::hr; // The device should have been acquired during the Poll()
}
return S_OK;
}
void Joystick::disConnectJoystick()
{
if (Joystick::joystick)
{
Joystick::joystick->Unacquire();
}
}
I have a simple C++ program reduced to 272 lines of code that initializes Direct2D, then performs a loop of 1000 operation where it simply creates an ID2D1GradientStopCollection followed by the creation of the using ID2D1LinearGradientBrush, then releases them immediately (Release count falls to zero on the calls to Release thus indicating that they should normally be unallocated).
However, after each execution of this loop, I see the memory reserved by the process increasing, and it is released only at the end, once the direct2D factories and devices are released, instead of being released after each call to linearGradientBrush->Release() and gradientStopCollection->Release() as I would expect.
The strange thing is that when I create/delete a SolidColorBrush instead of a LinearGradientBrush (still creating/deleting the gradientStopCollection) then there is no more memory leak observed.
Is this something I did wrong, causing for example the gradientStopCollection to still be allocated because still used by the brush? (But I do not see in that case why the reference count after the call to Release on the two objects falls to zero; and also, if I add an AddRef or remove a Release in order to trigger the wrong count of AddRef/Release on the objects (and get a non zero reference count situation at the end of the program) then the Direct2D debug layers indicates a fault inside d2d1debug3.dll calling kernelbase.dll on the call stack, thus showing that the AddRef/Release count is correct; yet I still observe this memory leak with the TaskManager).
Any help on how to resolve this would be appreciated
#include <windows.h>
#include <commctrl.h>
// DirectX header files.
#include <d2d1_1.h>
#include <d3d11.h>
#include <d3d11_1.h>
bool performAllocUnallocTestLoop(ID2D1DeviceContext* in_deviceContext) {
ID2D1GradientStopCollection* gradientStopCollection;
int i;
ID2D1LinearGradientBrush* linearGradientBrush;
int cnt;
ULONG nb;
D2D1_GRADIENT_STOP gradientStops[2];
HRESULT hr;
ID2D1SolidColorBrush* solidColorBrush;
gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Yellow, 1);
gradientStops[0].position = 0.0f;
gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::ForestGreen, 1);
gradientStops[1].position = 1.0f;
cnt = 1000;
for (i = 0; i < cnt; i++) {
hr = in_deviceContext->CreateGradientStopCollection(gradientStops, 2, D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, &gradientStopCollection);
if (!SUCCEEDED(hr)) {
return false;
}
hr = in_deviceContext->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2D1::Point2F(0, 0), D2D1::Point2F(150, 150)), gradientStopCollection, &linearGradientBrush);
if (!SUCCEEDED(hr)) {
gradientStopCollection->Release();
return false;
}
/*
// This code is commented and creates a solidColorBrush instead of a linearGradientBrush.
// Uncomment this code and comment the creation of the linearGradientBrush above instead and the leak disappears.
hr = in_deviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Yellow, 1), &solidColorBrush);
if (!SUCCEEDED(hr)) {
gradientStopCollection->Release();
return false;
}
*/
nb = linearGradientBrush->Release(); // Comment this line and the linearGradientBrush creation, then uncomment the solidColorBrush creation/release instead
// and the memory leak disappears.
// nb = solidColorBrush->Release();
nb = gradientStopCollection->Release();
}
return true;
}
int main_test_function(ID2D1DeviceContext* in_deviceContext) {
int i;
MessageBoxW(NULL, (WCHAR*)L"Before", (WCHAR*)L"Before", MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND);
for (i = 0; i < 10; i++) {
if (!performAllocUnallocTestLoop(in_deviceContext)) {
MessageBoxW(NULL, (WCHAR*)L"Some creation failed", (WCHAR*)L"Some creation failed", MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND);
return 0;
}
if (MessageBoxW(NULL, (WCHAR*)L"After performed another 1000 create/delete of gradientStopCollection+linearGradientBrush (leak observed in TaskManager, resource NOT released)", (WCHAR*)L"After", MB_OKCANCEL | MB_ICONINFORMATION | MB_SETFOREGROUND) == IDCANCEL) {
break;
}
}
return 0;
}
// _____________________________________________________________
// _ WinMain part: init/dest D3D and D2D factories and devices.
// _____________________________________________________________
struct TD2DFactoriesAndDevices {
ID2D1Factory1* d2dFactory;
ID3D11Device1* d3dDevice;
IDXGIDevice* dxgiDevice;
ID2D1Device* d2dDevice;
ID2D1DeviceContext* d2dDeviceContext;
};
void destFactoriesAndDevices(TD2DFactoriesAndDevices* in_struct) {
if (in_struct == NULL) {
return;
}
if (in_struct->d3dDevice != NULL) {
in_struct->d3dDevice->Release();
in_struct->d3dDevice = NULL;
}
if (in_struct->dxgiDevice != NULL) {
in_struct->dxgiDevice->Release();
in_struct->dxgiDevice = NULL;
}
if (in_struct->d2dDevice != NULL) {
in_struct->d2dDevice->Release();
in_struct->d2dDevice = NULL;
}
if (in_struct->d2dDeviceContext != NULL) {
in_struct->d2dDeviceContext->Release();
in_struct->d2dDeviceContext = NULL;
}
if (in_struct->d2dFactory != NULL) {
in_struct->d2dFactory->Release();
in_struct->d2dFactory = NULL;
}
CoUninitialize();
}
bool initFactoriesAndDevices(TD2DFactoriesAndDevices* in_struct,
const wchar_t*& out_error) {
HRESULT hr;
D2D1_FACTORY_OPTIONS options;
ID3D11Device* d3dDevice;
UINT createDeviceFlags;
D3D_DRIVER_TYPE driverType;
if (in_struct == NULL) {
out_error = L"Can not use NULL pointer";
return false;
}
in_struct->d3dDevice = NULL;
in_struct->dxgiDevice = NULL;
in_struct->d2dDevice = NULL;
in_struct->d2dDeviceContext = NULL;
in_struct->d2dFactory = NULL;
// init DCOM
if (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) != S_OK) {
out_error = L"Could not init DCOM";
return false;
}
// Create the Direct2D factory.
ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
#if defined(_DEBUG)
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
// options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
#endif
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
options, &in_struct->d2dFactory);
if (!SUCCEEDED(hr)) {
in_struct->d2dFactory = NULL;
out_error = L"D2D1CreateFactory failed";
CoUninitialize();
return false;
}
__create_devices:
// Create the D3D device on default adapter.
createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
createDeviceFlags |= D3D11_CREATE_DEVICE_SINGLETHREADED;
#ifdef _DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
// Creating the d3dDevice
in_struct->d3dDevice = NULL;
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
0,
createDeviceFlags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&d3dDevice,
nullptr,
nullptr
);
if (FAILED(hr)) {
in_struct->d3dDevice = NULL;
goto __create_failed;
}
hr = d3dDevice->QueryInterface(__uuidof(ID3D11Device1),
(void**)(&in_struct->d3dDevice));
if (FAILED(hr)) {
in_struct->d3dDevice = NULL;
goto __create_failed;
}
d3dDevice->Release();
// Get a DXGI device interface from the D3D device.
in_struct->dxgiDevice = NULL;
hr = in_struct->d3dDevice->QueryInterface(__uuidof(IDXGIDevice),
(void**)(&in_struct->dxgiDevice));
if (FAILED(hr)) {
in_struct->dxgiDevice = NULL;
goto __create_failed;
}
// Create a D2D device from the DXGI device.
hr = in_struct->d2dFactory->CreateDevice(in_struct->dxgiDevice,
&in_struct->d2dDevice);
if (FAILED(hr)) {
in_struct->d2dDevice = NULL;
goto __create_failed;
}
// Create a device context from the D2D device, to create the
// resources.
hr = in_struct->d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &in_struct->d2dDeviceContext);
if (FAILED(hr)) {
in_struct->d2dDeviceContext = NULL;
goto __create_failed;
}
// Setting isCreated
__after_create_devices:
return true;
__create_failed:
destFactoriesAndDevices(in_struct);
out_error = L"Could not create the devices";
return false;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
int result;
const wchar_t* _error;
TD2DFactoriesAndDevices _struct;
if (!initFactoriesAndDevices(&_struct, _error)) {
MessageBoxW(NULL, (WCHAR*)L"An error occured", _error,
MB_OK | MB_ICONINFORMATION);
return 0;
}
result = main_test_function(_struct.d2dDeviceContext);
destFactoriesAndDevices(&_struct);
return result;
}
This has been tested on two different machines, and the leak appears on x86/x64, debug/release plateforms
This is expected behaviour, considering your code.
Internally, Direct3D11 does some resource tracking, and calling release will only mark the resource for deletion, resource will be effectively deleted if the following conditions are met:
DeviceContext is Flushed
Resource external counter is 0 (the one returned by Release)
Resource internal counter is 0, this is incremented when your resource is attached, detached from the pipeline (in your case Direct2D handle it)
Since in your loop you do not flush at all, the resources are created on the GPU, but only marked for deletion.
There are several ways to flush device:
When you call EndDraw (or present on the Swapchain), to display on the screen.
When you manually perform Flush on the Direct3D Device Context (please note that you should not call Flush manually if you actually perform rendering on the screen, since Present/EndDraw will take care of that)
When you map a resource for cpu readback (out of scope in your use case I guess)
When the internal command buffer is full (since in your case you do not perform any drawing calls, if never gets filled).
In Direct3d11 case, you should also eventually call ClearState (this resets the whole pipeline tho), but since Direct2D normally takes care of unassigning internal resources, this should not be needed either in your use case.
I am using IMFSourceReaderCallback asynchroneous c++ implementation to read and process an USB Camera video stream
It is working fine, except that if the camera gets unplugged (which can happen often, since we are using many USB Repeaters), I am not being notified. Here is an extract of the code :
HRESULT CMFSourceReaderCallback::OnEvent(DWORD dwStreamIndex, IMFMediaEvent *pEvent)
{
// I was hoping to get an Event here if I lost the camera, but no...
// The break point nether hits, and from EvilCorp documentation, there is no such event type
return S_OK;
}
HRESULT CMFSourceReaderCallback::OnReadSample(
HRESULT hrStatus,
DWORD dwStreamIndex,
DWORD dwStreamFlags,
LONGLONG llTimestamp,
IMFSample *pSample)
{
bool isGrabbing = false;
try
{
if (SUCCEEDED(hrStatus))
{
if (pSample)
{
// Do something with the sample.
IMFMediaBuffer * pMediaBuffer;
HRESULT hr;
hr = pSample->ConvertToContiguousBuffer(&pMediaBuffer);
if (FAILED(hr))
{
// Inform other thread of the disconnection
//...
return S_OK;
}
byte *imgBuff;
DWORD buffCurrLen = 0;
DWORD buffMaxLen = 0;
pMediaBuffer->Lock(&imgBuff, &buffMaxLen, &buffCurrLen);
// Process image byte buffer
pMediaBuffer->Unlock();
pMediaBuffer->Release();
}
}
else
{
// Inform other thread of the disconnection
//...
return S_OK;
}
if ((MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags) ||
(MF_SOURCE_READERF_ERROR & dwStreamFlags))
{
// Inform other thread of the disconnection
//...
return S_OK;
}
} catch (std::exception &ex )
{
// Inform other thread of the disconnection
//...
return S_OK;
}
// check if other thread has not requested a stop
isGrabbing = ...;
if (isGrabbing)
{
//Re-arm callback
HRESULT hr = _dataStruct->Reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, NULL, NULL, NULL, NULL);
if (FAILED(hr))
{
// Inform other thread of the disconnection
//...
return S_OK;
}
}
return S_OK;
}
Is there a way to get such notification through IMFSourceReader without the headache of polling available devices with MFEnumDeviceSources from time to time, which can be time consuming... ?
Thanks In advance !
The recommended way to handle capture device loss is explained here : Handling Video Device Loss
You need to register for device notification : RegisterDeviceNotification. You need a window handle (HWND).
In the message loop, you will receive WM_DEVICECHANGE. You have to check the symbolic link (is it the USB camera ?).
When you are done, call UnregisterDeviceNotification.
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