I'm using C++ with MSXML and want to get data from XML file as Base64. I'm using this code:
hr = nodeList->get_item((long)i, (IXMLDOMNode **)&vXMLNode);
if ( FAILED(hr) )
throw "Unable to retrieve child node";
vXMLNode->put_dataType(_bstr_t("bin.base64"));
hr = vXMLNode->get_nodeTypedValue(&varValue);
if ( FAILED(hr) )
throw "Unable to retrieve 'xmltext' text";
So vXMLNode is a child node with data. Node type of vXMLNode is NODE_ELEMENT. If I check data type for this node, I get VT_NULL. Therefore I get my data as a string.
What is wrong in my code?
I believe the problem lies outside of your supplied sample code. Here's a working console program that uses nodeTypedValue. I hope this helps.
// Base64CPP.cpp : Defines the entry point for the console application.
// NOTE:
// This code is provided as is in the form of a sample with no warranties of any kind,
// either expressed or implied for any purpose. Use at your own risk.
#include "stdafx.h"
#include <TCHAR.H>
#include <stdio.h>
#include <sys/stat.h>
#include <fstream>
#include <string.h>
#include <atlbase.h>
#import <msxml4.dll> named_guids
using namespace MSXML2;
inline void EVAL_HR( HRESULT _hr )
{ if FAILED(_hr) throw(_hr); }
// Binary to Base64 Encoding
bool DoEncoding(LPCTSTR lpszXMLFile, LPCTSTR lpszBinFile)
{
bool bRetVal = false;
try
{
long lFileSize;
struct stat fileSizeVal;
// Get the file size
if (stat(lpszBinFile, &fileSizeVal) == 0)
{
lFileSize = fileSizeVal.st_size;
if(lFileSize <= 0)
return false;
}
else
return false;
// Instantiate XML DOMDocument
CComPtr<IXMLDOMDocument2> spXMLDoc;
CComPtr<MSXML2::IXMLDOMElement> spDataElement;
HRESULT hr = spXMLDoc.CoCreateInstance(CLSID_DOMDocument40);
EVAL_HR(hr);
spXMLDoc->loadXML(_T("<?xml version='1.0' encoding='UTF-8'?> <Data/>"));
// Prepare the top node to store binary data as base64
spDataElement = spXMLDoc->documentElement;
ATLASSERT(spDataElement != NULL && _T("Failed to get document element."));
hr = spDataElement->put_dataType(CComBSTR(_T("bin.base64")));
EVAL_HR(hr);
// Read the file into memory
std::ifstream binDataFile;
binDataFile.open(lpszBinFile, std::ios::binary);
binDataFile.seekg(std::ios::beg);
SAFEARRAY* psa = SafeArrayCreateVector( VT_UI1, 0L, lFileSize);
binDataFile.read((char*)psa->pvData, lFileSize);
binDataFile.close();
// Save the binary data into Data node
VARIANT var;
VariantInit(&var);
var.parray = psa;
var.vt = (VT_ARRAY | VT_UI1 );
spDataElement->nodeTypedValue = var;
// Save the XML Document
spXMLDoc->save(lpszXMLFile);
bRetVal = true;
}
catch(int)
{
//TODO: Error handling
printf(_T("Exception raised!"));
}
return bRetVal;
}
// Base64 to binary decoding
bool DoDecoding(LPCTSTR lpszXMLFile, LPCTSTR lpszBinFile)
{
bool bRetVal = false;
try
{
// Instantiate DOMDocument and load XML file
CComPtr<IXMLDOMDocument2> spXMLDoc;
CComPtr<MSXML2::IXMLDOMElement> spDataElement;
HRESULT hr = spXMLDoc.CoCreateInstance(CLSID_DOMDocument40);
EVAL_HR(hr);
CComVariant vtFileName = CComBSTR(lpszXMLFile);
spXMLDoc->load(vtFileName);
// ASSUMPTION: documentElement (top element) contains base64 data
spDataElement = spXMLDoc->documentElement;
ATLASSERT(spDataElement != NULL && _T("Failed to get document element."));
// Prepare the file for writing
std::ofstream binDataFile (lpszBinFile, std::ios::out | std::ios::binary);
// Save the binary data into data file
long lDataLength=0;
hr = SafeArrayGetUBound(spDataElement->nodeTypedValue.parray, 1, &lDataLength);
EVAL_HR(hr);
lDataLength++;
binDataFile.write((char*)(spDataElement->nodeTypedValue.parray->pvData), lDataLength);
binDataFile.close();
bRetVal = true;
}
catch(int)
{
//TODO: Error handling
printf(_T("Exception raised!"));
}
return bRetVal;
}
int main(int argc, char* argv[])
{
if(argc < 4)
{//insufficient parameters
printf(_T("\nSample app to encode/decode binary data as base64 XML using MSXML.\n"));
printf(_T("\nUsage:\tBase64CPP.exe <e>|<d> <XMLFileName> <BinaryDataFile>"));
printf(_T("\n\nExamples:"));
printf(_T("\n\tEncode binary data in 1.jpg and save as Base64 into 1.xml:"));
printf(_T("\n\t\tBase64CPP.exe e c:\\1.xml c:\\1.jpg\n"));
printf(_T("\n\tDecode base64 data in 1.xml and save as binary into 2.jpg:"));
printf(_T("\n\t\tBase64CPP.exe d c:\\1.xml c:\\2.jpg\n\n"));
return -1;
}
try
{
HRESULT hr = CoInitialize(NULL);
EVAL_HR(hr);
if(_tcsicmp(argv[1], _T("e")) == 0)
{
if(DoEncoding(argv[2], argv[3]))
printf(_T("Success. See %s for results."), argv[2]);
else
printf(_T("Failed."));
}
else
{
if(DoDecoding(argv[2], argv[3]))
printf(_T("Success. See %s for results."), argv[3]);
else
printf(_T("Failed."));
}
}
catch(int)
{
//TODO: Error handling
printf(_T("Exception raised!"));
}
CoUninitialize();
getchar();
return 0;
}
Related
I compiled IIS Native Module. If I compile 32bit and install in IIS. It gives error
The Module DLL ... failed to load. The data is the error. Data is 7E000000
If I compile 64 bit and install it in IIS, I have no error but module is not working.
I added code to write to log. When I use C:\Windows\System32\inetsrv\w3wp.exe in debugging property and debug it works and writes to log but when I use it in IIS neither RegisterModule is invoked, nor any of the notifications.
Any idea what may be wrong?
In Visual Studio elevated mode I can debug the dll just fine. Both 32 bit and 64 bit work just fine in debugging. It returns the data I want to return instead of returning actual page content. It is strange that same w3wp.exe running in IIS doesn't work with dll.
Update:
It works in Windows 7 IIS but doesn't work on Windows 10 IIS. Wondering what may be the difference.
main.cpp
#include "precomp.h"
#include <iostream>
#include <fstream>
#include <time.h>
using namespace std;
// Global server instance
IHttpServer * g_pHttpServer = NULL;
// Global module context id
PVOID g_pModuleContext = NULL;
#define DTTMFMT "%Y-%m-%d %H:%M:%S "
#define DTTMSZ 21
static char* getDtTm(char* buff) {
time_t t = time(0);
strftime(buff, DTTMSZ, DTTMFMT, localtime(&t));
return buff;
}
// The RegisterModule entrypoint implementation.
// This method is called by the server when the module DLL is
// loaded in order to create the module factory,
// and register for server events.
HRESULT
__stdcall
RegisterModule(
DWORD dwServerVersion,
IHttpModuleRegistrationInfo * pModuleInfo,
IHttpServer * pHttpServer
)
{
char buff[DTTMSZ];
ofstream myfile;
myfile.open("C:\\inetpub\\logs\\JCFilter.txt", ios::app);
myfile << getDtTm(buff);
myfile << " RegisterModule.\n";
myfile.close();
HRESULT hr = S_OK;
CJCFilterFactory * pFactory = NULL;
if ( pModuleInfo == NULL || pHttpServer == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
goto Finished;
}
// step 1: save the IHttpServer and the module context id for future use
g_pModuleContext = pModuleInfo->GetId();
g_pHttpServer = pHttpServer;
// step 2: create the module factory
pFactory = new CJCFilterFactory();
if ( pFactory == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Finished;
}
// step 3: register for server events
// TODO: register for more server events here
hr = pModuleInfo->SetRequestNotifications( pFactory, /* module factory */
RQ_ACQUIRE_REQUEST_STATE | RQ_BEGIN_REQUEST/* server event mask */,
0 /* server post event mask */);
if ( FAILED( hr ) )
{
goto Finished;
}
// Set the request priority.
hr = pModuleInfo->SetPriorityForRequestNotification(
RQ_BEGIN_REQUEST, PRIORITY_ALIAS_FIRST);
if (FAILED(hr))
{
goto Finished;
}
pFactory = NULL;
Finished:
if ( pFactory != NULL )
{
delete pFactory;
pFactory = NULL;
}
return hr;
}
JCFilter.cpp
#include "precomp.h"
#include <iostream>
#include <fstream>
#include <time.h>
using namespace std;
#define DTTMFMT "%Y-%m-%d %H:%M:%S "
#define DTTMSZ 21
static char* getDtTm(char* buff) {
time_t t = time(0);
strftime(buff, DTTMSZ, DTTMFMT, localtime(&t));
return buff;
}
// Implementation of the OnAcquireRequestState method
REQUEST_NOTIFICATION_STATUS
CJCFilter::OnAcquireRequestState(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
)
{
char buff[DTTMSZ];
ofstream myfile;
myfile.open("C:\\inetpub\\logs\\JCFilter.txt", ios::app);
myfile << getDtTm(buff);
myfile << " OnAcquireRequestState.\n";
myfile.close();
HRESULT hr = S_OK;
// TODO: implement the AcquireRequestState module functionality
Finished:
if ( FAILED( hr ) )
{
return RQ_NOTIFICATION_FINISH_REQUEST;
}
else
{
return RQ_NOTIFICATION_CONTINUE;
}
}
REQUEST_NOTIFICATION_STATUS
CJCFilter::OnBeginRequest(
IN IHttpContext* pHttpContext,
IN IHttpEventProvider* pProvider
)
{
char buff[DTTMSZ];
ofstream myfile;
myfile.open("C:\\inetpub\\logs\\JCFilter.txt", ios::app);
myfile << getDtTm(buff);
myfile << "OnBeginRequest.\n";
myfile.close();
UNREFERENCED_PARAMETER(pProvider);
// Create an HRESULT to receive return values from methods.
HRESULT hr;
// Retrieve a pointer to the response.
IHttpResponse* pHttpResponse = pHttpContext->GetResponse();
// Test for an error.
if (pHttpResponse != NULL)
{
// Clear the existing response.
pHttpResponse->Clear();
// Set the MIME type to plain text.
pHttpResponse->SetHeader(
HttpHeaderContentType, "text/plain",
(USHORT)strlen("text/plain"), TRUE);
// Create a string with the response.
PCSTR pszBuffer = "Hello World!";
// Create a data chunk.
HTTP_DATA_CHUNK dataChunk;
// Set the chunk to a chunk in memory.
dataChunk.DataChunkType = HttpDataChunkFromMemory;
// Buffer for bytes written of data chunk.
DWORD cbSent;
// Set the chunk to the buffer.
dataChunk.FromMemory.pBuffer =
(PVOID)pszBuffer;
// Set the chunk size to the buffer size.
dataChunk.FromMemory.BufferLength =
(USHORT)strlen(pszBuffer);
// Insert the data chunk into the response.
hr = pHttpResponse->WriteEntityChunks(
&dataChunk, 1, FALSE, TRUE, &cbSent);
// Test for an error.
if (FAILED(hr))
{
// Set the HTTP status.
pHttpResponse->SetStatus(500, "Server Error", 0, hr);
}
// End additional processing.
return RQ_NOTIFICATION_FINISH_REQUEST;
}
// Return processing to the pipeline.
return RQ_NOTIFICATION_CONTINUE;
}
// TODO: implement other desired event handler methods below
I have been trying lately to get a grasp on the Windows Property System. I implemented a custom file type(Property Handler, Thumbnail Handler, Preview Handler) and I want to hide System.Size property for my file type on Windows Explorer(Details Tab on Properties dialogbox and Size Column on Details View Mode).I used the following code but no result. Is it possible to hide size property? Maybe I should write Shell Folder extension.
#include <shobjidl.h>
#include <propsys.h>
#include <propvarutil.h>
#include <propkey.h>
#include <strsafe.h>
#pragma comment(lib,"propsys.lib")
HRESULT SetPropertyValue(PCWSTR pszFilename, PCWSTR pszCanonicalName, PCWSTR pszValue)
{
// Convert the Canonical name of the property to PROPERTYKEY
PROPERTYKEY key;
HRESULT hr = PSGetPropertyKeyFromName(pszCanonicalName, &key);
if (SUCCEEDED(hr))
{
IPropertyStore* pps = NULL;
hr = SHGetPropertyStoreFromParsingName(pszFilename, NULL, gpsFlags, IID_PPV_ARGS(ppps));
if (SUCCEEDED(hr))
{
PROPVARIANT propvarValue = { 0 };
hr = InitPropVariantFromString(pszValue, &propvarValue);
if (SUCCEEDED(hr))
{
hr = PSCoerceToCanonicalValue(key, &propvarValue);
if (SUCCEEDED(hr))
{
// Set the value to the property store of the item.
hr = pps->SetValue(key, propvarValue);
if (SUCCEEDED(hr))
{
// Commit does the actual writing back to the file stream.
hr = pps->Commit();
if (SUCCEEDED(hr))
{
wprintf(L"Property %s value %s written successfully \n", pszCanonicalName, pszValue);
}
else
{
wprintf(L"Error %x: Commit to the propertystore failed.\n", hr);
}
}
else
{
wprintf(L"Error %x: Set value to the propertystore failed.\n", hr);
}
}
PropVariantClear(&propvarValue);
}
pps->Release();
}
else
{
wprintf(L"Error %x: getting the propertystore for the item.\n", hr);
}
}
else
{
wprintf(L"Invalid property specified: %s\n", pszCanonicalName);
}
return hr;
}
int wmain(int argc, wchar_t* argv[])
{
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
{
SetPropertyValue(L"FullFilePath",L"System.Size", L"0");
CoUninitialize();
}
return 0;
}
I am new to the concept of MSMQ(Microsoft Message Queues). I had a glimpse of the sample code from Microsoft's link and am trying to create a simple MSMQ queue. Here is my full code (mostly from Microsoft's link).
#include "stdafx.h"
#include "windows.h"
#include "mq.h"
#pragma comment (lib, "Mqrt.lib")
#include "tchar.h"
#include <stdio.h>
#define BUFLEN = 256;
#include <iostream>
HRESULT CreateMSMQQueue(
LPWSTR wszPathName,
PSECURITY_DESCRIPTOR pSecurityDescriptor,
LPWSTR wszOutFormatName,
DWORD *pdwOutFormatNameLength
)
{
// Define the maximum number of queue properties.
const int NUMBEROFPROPERTIES = 2;
// Define a queue property structure and the structures needed to initialize it.
MQQUEUEPROPS QueueProps;
MQPROPVARIANT aQueuePropVar[NUMBEROFPROPERTIES];
QUEUEPROPID aQueuePropId[NUMBEROFPROPERTIES];
HRESULT aQueueStatus[NUMBEROFPROPERTIES];
HRESULT hr = MQ_OK;
// Validate the input parameters.
if (wszPathName == NULL || wszOutFormatName == NULL || pdwOutFormatNameLength == NULL)
{
return MQ_ERROR_INVALID_PARAMETER;
}
// Set queue properties.
DWORD cPropId = 0;
aQueuePropId[cPropId] = PROPID_Q_PATHNAME;
aQueuePropVar[cPropId].vt = VT_LPWSTR;
aQueuePropVar[cPropId].pwszVal = wszPathName;
cPropId++;
WCHAR wszLabel[MQ_MAX_Q_LABEL_LEN] = L"Test Queue";
aQueuePropId[cPropId] = PROPID_Q_LABEL;
aQueuePropVar[cPropId].vt = VT_LPWSTR;
aQueuePropVar[cPropId].pwszVal = wszLabel;
cPropId++;
// Initialize the MQQUEUEPROPS structure.
QueueProps.cProp = cPropId; // Number of properties
QueueProps.aPropID = aQueuePropId; // IDs of the queue properties
QueueProps.aPropVar = aQueuePropVar; // Values of the queue properties
QueueProps.aStatus = aQueueStatus; // Pointer to the return status
// Call MQCreateQueue to create the queue.
WCHAR wszFormatNameBuffer[256];
DWORD dwFormatNameBufferLength = 256;
hr = MQCreateQueue(pSecurityDescriptor, // Security descriptor
&QueueProps, // Address of queue property structure
wszFormatNameBuffer, // Pointer to format name buffer
&dwFormatNameBufferLength); // Pointer to receive the queue's format name length in Unicode characters not bytes.
// Return the format name if the queue is created successfully.
if (hr == MQ_OK || hr == MQ_INFORMATION_PROPERTY)
{
if (*pdwOutFormatNameLength >= dwFormatNameBufferLength)
{
wcsncpy_s(wszOutFormatName, *pdwOutFormatNameLength - 1, wszFormatNameBuffer, _TRUNCATE);
wszOutFormatName[*pdwOutFormatNameLength - 1] = L'\0';
*pdwOutFormatNameLength = dwFormatNameBufferLength;
}
else
{
wprintf(L"The queue was created, but its format name cannot be returned.\n");
}
}
return hr;
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<<"started!";
LPWSTR sam = L".\\myQueue";
LPWSTR out_name = L"Sampleoutput";
DWORD d1 = 60;
DWORD *dd = &d1;
HRESULT hs = CreateMSMQQueue(sam,pt,out_name,dd);
return 0;
}
I didn't change much of the code. When I run this code, I get an "The application was unable to start correctly(0x0000142)" error. By debugging I found out tat its due to an "Memory access error" but there is no hint as to where it occurs. Please help me out!
Is there any format for queue names? Or the output format name ?
I have a school project that I am working on and the outcome is pointless it seems, but it's got more to do with the experience gained through this I believe.
What I am trying to do is submit an initial URL, then pull all the URLs on that page and visit them in order and do this until I tell it to stop. All of the URLs will be recorded in a text file.
So far, I am able to open a window in IE and launch a webpage of my choosing. So now I need to know how to send IE to a new webpage using the same session and also how I can scan and pull data from the websites I visit. Thanks for any help!
Here is my code so far:
#include <string>
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
using namespace std;
int main( int argc, TCHAR *argv[] )
{
std::string uRL, prog;
int length, count;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
//if( argc != 2 )
//{
// printf("Usage: %s [cmdline]\n", argv[0]);
// system("PAUSE");
// return 0;
//}
std::cout << "Enter URL: ";
std::cin >> uRL;
prog = ("C:\\Program Files\\Internet Explorer\\iexplore.exe ") + uRL;
char *cstr = new char[prog.length() + 1];
strcpy(cstr, prog.c_str());
// Start the child process.
if( !CreateProcess(NULL, // No module name (use command line)
_T(cstr), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
system("PAUSE");
return 0;
}
cout << HRESULT get_Count(long *Count) << endl;
//cout << count << endl;
system("PAUSE");
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
delete [] cstr;
return 0;
}
If you want to crawl a webpage launching Internet Explorer is not going to work very well. I also don't recommend attempting to parse the HTML page yourself unless you are prepared for a lot of heartache and hassle. Instead I recommend that you create an instance of an IWebBrowser2 object and use it to navigate to the webpage, grab the appropriate IHTMLDocument2 object and iterate through the elements picking out the URL's. It's far easier and is a common approach using components that are already installed on Windows. The example below should get your started and on your way to crawling the web like proper spider should.
#include <comutil.h> // _variant_t
#include <mshtml.h> // IHTMLDocument and IHTMLElement
#include <exdisp.h> // IWebBrowser2
#include <atlbase.h> // CComPtr
#include <string>
#include <iostream>
#include <vector>
// Make sure we link in the support library!
#pragma comment(lib, "comsuppw.lib")
// Load a webpage
HRESULT LoadWebpage(
const CComBSTR& webpageURL,
CComPtr<IWebBrowser2>& browser,
CComPtr<IHTMLDocument2>& document)
{
HRESULT hr;
VARIANT empty;
VariantInit(&empty);
// Navigate to the specifed webpage
hr = browser->Navigate(webpageURL, &empty, &empty, &empty, &empty);
// Wait for the load.
if(SUCCEEDED(hr))
{
READYSTATE state;
while(SUCCEEDED(hr = browser->get_ReadyState(&state)))
{
if(state == READYSTATE_COMPLETE) break;
}
}
// The browser now has a document object. Grab it.
if(SUCCEEDED(hr))
{
CComPtr<IDispatch> dispatch;
hr = browser->get_Document(&dispatch);
if(SUCCEEDED(hr) && dispatch != NULL)
{
hr = dispatch.QueryInterface<IHTMLDocument2>(&document);
}
else
{
hr = E_FAIL;
}
}
return hr;
}
void CrawlWebsite(const CComBSTR& webpage, std::vector<std::wstring>& urlList)
{
HRESULT hr;
// Create a browser object
CComPtr<IWebBrowser2> browser;
hr = CoCreateInstance(
CLSID_InternetExplorer,
NULL,
CLSCTX_SERVER,
IID_IWebBrowser2,
reinterpret_cast<void**>(&browser));
// Grab a web page
CComPtr<IHTMLDocument2> document;
if(SUCCEEDED(hr))
{
// Make sure these two items are scoped so CoUninitialize doesn't gump
// us up.
hr = LoadWebpage(webpage, browser, document);
}
// Grab all the anchors!
if(SUCCEEDED(hr))
{
CComPtr<IHTMLElementCollection> urls;
long count = 0;
hr = document->get_all(&urls);
if(SUCCEEDED(hr))
{
hr = urls->get_length(&count);
}
if(SUCCEEDED(hr))
{
for(long i = 0; i < count; i++)
{
CComPtr<IDispatch> element;
CComPtr<IHTMLAnchorElement> anchor;
// Get an IDispatch interface for the next option.
_variant_t index = i;
hr = urls->item( index, index, &element);
if(SUCCEEDED(hr))
{
hr = element->QueryInterface(
IID_IHTMLAnchorElement,
reinterpret_cast<void **>(&anchor));
}
if(SUCCEEDED(hr) && anchor != NULL)
{
CComBSTR url;
hr = anchor->get_href(&url);
if(SUCCEEDED(hr) && url != NULL)
{
urlList.push_back(std::wstring(url));
}
}
}
}
}
}
int main()
{
HRESULT hr;
hr = CoInitialize(NULL);
std::vector<std::wstring> urls;
CComBSTR webpage(L"http://cppreference.com");
CrawlWebsite(webpage, urls);
for(std::vector<std::wstring>::iterator it = urls.begin();
it != urls.end();
++it)
{
std::wcout << "URL: " << *it << std::endl;
}
CoUninitialize();
return 0;
}
To scan and pull data from the websites, you'll want to capture the HTML and iterate through it looking for all character sequences matching a certain pattern. Have you ever used regular expressions? Regular expressions would by far be the best here, but if you understand them (just look up a tutorial on the basics) then you can manually apply the pattern-recognition concepts to this project.
So what you're looking for is something like http(s)://.. It's more complex though, because domain names are a rather intricate pattern. You'll probably want to use a third-party HTML parser or regular expression library, but it's doable without it, although pretty tedious to program.
Here's a link about regular expressions in c++:
http://www.johndcook.com/cpp_regex.html
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