Count programs running by it's className - c++

I am trying to detect how many instances of a application person is running. Did he open my application once? Twice? Thrice?
I tried to detect it by checking it's instances by process names, but in windows it is pointles - people might change .exe name and it won't count towards final number.
How would I proceed then? I thought about searching it by className (HWND?) rather by processName, but how would I do it?
This is the code I am using for detecting by process name:
int Platform::getMulticlientCount(const std::string& ProcessName)
{
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
const char *cstr = ProcessName.c_str();
int counter = 0;
if (Process32First(hSnapshot, &pe32))
{
do
{
if (_tcsicmp(pe32.szExeFile, cstr) == 0)
{
counter++;
}
} while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
return counter;
}

The instance of the Remy:
static int counter;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
char classname[MAX_PATH] = { 0 };
GetClassNameA(hwnd,classname, MAX_PATH);
if (_tcsicmp(classname, (char*)lParam) == 0)
counter++;
return true;
}
int Platform::getMulticlientCount(const std::string& ClassName)
{
counter = 0;
const char *cstr = ClassName.c_str();
EnumWindows(EnumWindowsProc, (LPARAM)cstr);
return counter;
}
If you also need to get the instance, in EnumWindowsProc:
HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
If you also need to get the processId, in EnumWindowsProc:
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);

Here is an example code that counts the instances that are running. While the application count the instances it self it does not matter if the binary is renamed. I used a file to keep the example simple but registry would work too. The only thing that is missing is a global mutex to protect the file against concurrent access. HTH
#include <iostream>
#include <thread>
#include <fstream>
#include <Windows.h>
class GlobalCounter
{
public:
GlobalCounter(const std::string& id)
: _id(id)
{
const auto filename = "C:\\users\\twollgam\\" + id + ".counter";
if (GlobalFindAtomA(id.c_str()) == 0)
{
std::ofstream(filename) << 1;
std::cout << "I am the first instance." << std::endl;
}
else
{
auto counter = 0;
std::ifstream(filename) >> counter;
++counter;
std::ofstream(filename) << counter;
std::cout << "I am the " << counter << " instance." << std::endl;
}
_atom = GlobalAddAtomA(id.c_str());
}
~GlobalCounter()
{
const auto filename = "C:\\users\\twollgam\\" + _id + ".counter";
auto counter = 0;
std::ifstream(filename) >> counter;
--counter;
std::ofstream(filename) << counter;
GlobalDeleteAtom(_atom);
}
private:
const std::string _id;
ATOM _atom;
};
int main()
{
const auto globalCounter = GlobalCounter("test");
std::cout << "Hello World!\n";
std::this_thread::sleep_for(std::chrono::seconds(30));
return 0;
}

Related

How to find current input language

I'm trying to make a program that gets a process name, finds it's ID,
and then finds the language with the function GetKeyboardLayout.
Although I'm having difficulties and it seem not to work.
It finds the processID although the language that return is always 00000000.
That is my code :
#include <iostream>
#include <windows.h>
#include <string>
#include <tlhelp32.h>
DWORD FindProcessId(LPCTSTR ProcessName);
int main() {
HKL currentKBLayout;
DWORD processID;
LPCTSTR processName = "chrome.exe";
while (true) {
processID = FindProcessId(processName);
if (processID == 0); // TODO: pause system for 5 seconds
else {
currentKBLayout = GetKeyboardLayout(processID);
std::cout << processID << " | "<< currentKBLayout << std::endl;
}
}
system("pause");
return 0;
}
DWORD FindProcessId(LPCTSTR ProcessName)
{
PROCESSENTRY32 pt;
HANDLE hsnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pt.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hsnap, &pt)) { // must call this first
do {
if (!lstrcmpi(pt.szExeFile, ProcessName)) {
CloseHandle(hsnap);
return pt.th32ProcessID;
}
} while (Process32Next(hsnap, &pt));
}
CloseHandle(hsnap); // close handle on failure
return 0;
}
I agree with Remys comment about using a simpler way to get the keyboard layout for the processes if that's all you need. If you however are interested in adding more information to your current approach using snapshots, this could be a way to start. It takes a snapshot of all processes and threads. Each Process has a vector of Thread objects. Adding Thread objects to each Process is done via an unordered_map<processId, Process>. To get a unique set of keyboard layouts for each process (since each thread may in theory have its own), an unordered_set<HKL> is used.
#include "pch.h"
#include <iostream>
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <windows.h>
#include <tlhelp32.h>
struct Thread {
DWORD m_id;
HKL m_keyboard_layout;
Thread(DWORD Id) :
m_id(Id), m_keyboard_layout(GetKeyboardLayout(m_id))
{}
};
struct Process {
std::vector<Thread> m_threads;
DWORD m_id;
std::wstring m_exefile;
Process() = default;
Process(DWORD Id, std::wstring Name) :
m_id(Id), m_exefile(Name)
{}
// get a unique set of HKL:s from all the threads in the process
std::unordered_set<HKL> GetKeyboardLayouts() const {
std::unordered_set<HKL> rv;
for (auto& t : m_threads) {
if(t.m_keyboard_layout) // does it have a keyboard layout?
rv.emplace(t.m_keyboard_layout);
}
return rv;
}
// if you'd like to iterate over the individual threads
std::vector<Thread>::iterator begin() { return m_threads.begin(); }
std::vector<Thread>::iterator end() { return m_threads.end(); }
};
class Snapshot {
HANDLE hSnap;
std::unordered_map<DWORD, Process> m_processes;
void GetProcesses() {
PROCESSENTRY32 pt;
pt.dwSize = sizeof(pt);
if (Process32First(hSnap, &pt)) { // must call this first
do {
m_processes[pt.th32ProcessID] = Process(pt.th32ProcessID, pt.szExeFile);
} while (Process32Next(hSnap, &pt));
}
}
void GetThreads() {
THREADENTRY32 pt;
pt.dwSize = sizeof(pt);
if (Thread32First(hSnap, &pt)) { // must call this first
do {
m_processes[pt.th32OwnerProcessID].m_threads.emplace_back(pt.th32ThreadID);
} while (Thread32Next(hSnap, &pt));
}
}
void Populate() {
GetProcesses();
GetThreads();
}
public:
Snapshot() :
hSnap(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0)),
m_processes()
{
// TODO: make this exception better
if (hSnap == INVALID_HANDLE_VALUE) throw GetLastError();
Populate();
CloseHandle(hSnap);
}
std::unordered_map<DWORD, Process>::iterator begin() { return m_processes.begin(); }
std::unordered_map<DWORD, Process>::iterator end() { return m_processes.end(); }
};
int main() {
Snapshot snap;
// show processes with keyboard layouts
for (const auto& m : snap) { // std::pair m.first = processId, m.second = Process
const Process& p = m.second;
auto layouts = p.GetKeyboardLayouts();
if (layouts.size()) { // only show processes with keyboard layouts
std::wcout << p.m_id << L" " << p.m_exefile << L"\n";
for (const auto& l : layouts) {
std::wcout << L" layout " << l << L"\n";
}
}
}
return 0;
}

boost interprocess message_queue and fork

I am trying to communicate with forked child process using message queue from boost interprocess library. When child process calls receive it causes exception with message
boost::interprocess_exception::library_error
I am using GCC 6.3 on Debian 9 x64.
#include <iostream>
#include <unistd.h>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <memory>
int main(int argc, char* argv[])
{
using namespace boost::interprocess;
const char* name = "foo-552b8ae9-6037-4b77-aa0d-d4dc9dad790b";
const int max_num_msg = 100;
const int max_msg_size = 32;
bool is_child = false;
message_queue::remove(name);
auto mq = std::make_unique<message_queue>(create_only, name, max_num_msg, max_msg_size);
auto child_pid = fork();
if (child_pid == -1)
{
std::cout << "fork failed" << std::endl;
return -1;
}
else if (child_pid == 0)
{
is_child = true;
}
if (is_child)
{
// does child needs to reopen it?
mq.reset( new message_queue(open_only, name) );
}
int send_num = 0;
while(true)
{
unsigned int priority = 0;
if (is_child)
{
message_queue::size_type bytes = 0;
try
{
int num;
// Always throws. What is wrong ???????
mq->receive(&num, sizeof(num), bytes, priority);
std::cout << num << std::endl;
}
catch(const std::exception& e)
{
std::cout << "Receive caused execption " << e.what() << std::endl;
}
sleep(1);
}
else
{
mq->send(&send_num, sizeof(send_num), priority);
send_num++;
sleep(5);
}
}
return 0;
}
Also, in child process is it required to reopen the message queue created by the parent process? I tried it both ways and neither worked. I am getting the same exception on receive.
The problem is that your receive buffer is smaller than max_msg_size. Assuming 4-byte integers, this should work:
int num[8];
mq.receive(num, sizeof(num), bytes, priority);
std::cout << *num << std::endl;
Also, I see no reason to play fast and loose with the actual queue instance. Just create it per process:
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include <iostream>
#include <memory>
#include <unistd.h>
int main() {
namespace bip = boost::interprocess;
const char *name = "foo-552b8ae9-6037-4b77-aa0d-d4dc9dad790b";
{
const int max_num_msg = 100;
const int max_msg_size = 32;
bip::message_queue::remove(name);
bip::message_queue mq(bip::create_only, name, max_num_msg, max_msg_size);
}
auto child_pid = fork();
if (child_pid == -1) {
std::cout << "fork failed" << std::endl;
return -1;
}
bip::message_queue mq(bip::open_only, name);
if (bool const is_child = (child_pid == 0)) {
while (true) {
unsigned int priority = 0;
bip::message_queue::size_type bytes = 0;
try {
int num[8];
mq.receive(num, sizeof(num), bytes, priority);
std::cout << *num << std::endl;
} catch (const bip::interprocess_exception &e) {
std::cout << "Receive caused execption " << boost::diagnostic_information(e, true) << std::endl;
}
sleep(1);
}
} else {
// parent
int send_num = 0;
while (true) {
unsigned int priority = 0;
mq.send(&send_num, sizeof(send_num), priority);
send_num++;
sleep(5);
}
}
}

error in wrapping class for dll functions

I have a 3rd party .dll (and relative .h and .lib) to control a device via USB.
I want to use the dll functions into a class (AOTF_controller) to implement my desired behaviour.
What I want to do is :
Connect to the device (connect() class function);
Initialize it (init() class function);
Set some parameters (setScanningFreq() class function)
Increase sequentially the frequency of my device (increaseFreq() class function)
Reset and close the USB connection.
I can obtain this behavior when I use the dll functions directly into the _tmain() therefore the device works correctly, but when I wrap the dll functions into a class and try to use the class something goes wrong.
I repeat the above process (list item 1-5) several times: sometimes it works fine, sometimes the program stop and the debugger gives me this error:
First-chance exception at 0x77533fb7 in AOTFcontrollerDebug.exe: 0xC0150014: The activation context activation stack for the running thread of execution is corrupt.
The error seems random, sometimes I can conclude 80 times the scan without any problem, sometimes it gives me error right at the first scan.
I tried to search for that error but I was not able to find anything useful.
Anyone can help? I guess could be related on how the dll functions are called in my class?
Here is the main function code:
// AOTFcontrollerDebug.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "AOTFcontrollerDebug.h"
#include "AOTF_Controller.h"
#include <iostream>
#include <string>
#include <sstream>
#include <AotfLibrary.h>
#define DEFAULT_STARTFREQUENCY 78
#define DEFAULT_ENDFREQUENCY 95.5
#define DEFAULT_NUMOFFRAMES 256
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HMODULE hModule = ::GetModuleHandle(NULL);
if (hModule != NULL)
{
// initialize MFC and print and error on failure
if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
std::cout << "-----AOTF Controller Debugging-----"<<endl;
//input of scans to do
int repeatedScan;
std::cout << "Type how many repeated scan: "<<endl;
std::cin >> repeatedScan;
//instance
AOTF_Controller m_AOTF_Controller;
std::cout << "AOTF Controller instance done..."<<endl;
//loop over scans
for(int i =0;i<repeatedScan;i++)
{
m_AOTF_Controller.connect();
std::cout << "AOTF Controller connected..."<<endl;
std::cout << "Scan number : "<< (i + 1) <<endl;
m_AOTF_Controller.init();
//set the delta freq to increase at each step
m_AOTF_Controller.setScanningFreq(DEFAULT_STARTFREQUENCY, DEFAULT_ENDFREQUENCY, DEFAULT_NUMOFFRAMES);
//loop over wavelengths
int sleep_ms = 4;
for (int j =0; j <DEFAULT_NUMOFFRAMES; j++)
{
Sleep(sleep_ms) ;
m_AOTF_Controller.increaseFreq();
//std::cout << " "<< (j + 1) ;
}
// terminate scans
m_AOTF_Controller.reset();
m_AOTF_Controller.disconnect();
std::cout << endl <<"Scan number "<< (i + 1) << "terminated successfully" <<endl;
Sleep(sleep_ms*100) ;
}
}
}
else
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
nRetCode = 1;
}
return nRetCode;
}
and here the Aotf_Controller class code:
//Aotf_Controller.h
#pragma once
#include <AotfLibrary.h>
#include <string>
#include <sstream>
#include <iomanip>
#define CONVERSION_MHZ_TO_HZ 1000000
class AOTF_Controller
{
private:
enum Error {SUCCESSFUL , CONNECTION_ERROR, DISCONNECTION_ERROR, INIT_ERROR, RESET_ERROR , SET_ERROR }; // error enum values
HANDLE hAotfController;
int currentGain;
long currentFreq; // current frequency in Hz
long startFreq, endFreq, deltaFreq; // frequency values for the scanning in Hz
public:
AOTF_Controller(void);
~AOTF_Controller(void);
AOTF_Controller::Error connect();
AOTF_Controller::Error disconnect();
AOTF_Controller::Error init();
AOTF_Controller::Error setFreq(float freq); // for frequency value in MHZ (precision to the 3rd decimal i.e. KHz)
AOTF_Controller::Error setFreq(long freq); // for frequency value in Hz
AOTF_Controller::Error setGain(int gain);
AOTF_Controller::Error reset();
AOTF_Controller::Error setScanningFreq(float _startFreq, float _endFreq, int numOfFrames);
AOTF_Controller::Error increaseFreq();
};
//Aotf_Controller.cpp
#include "StdAfx.h"
#include "AOTF_Controller.h"
AOTF_Controller::AOTF_Controller(void)
{
currentGain = 0;
currentFreq = 0;
startFreq = 0;
endFreq = 0;
deltaFreq = 0;
hAotfController = NULL;
}
AOTF_Controller::~AOTF_Controller(void)
{
}
AOTF_Controller::Error AOTF_Controller::connect()
{
int iInstance = 0;
hAotfController = AotfOpen(iInstance);
if (!hAotfController)
{
return CONNECTION_ERROR;
}
else
{
return SUCCESSFUL;
}
}
AOTF_Controller::Error AOTF_Controller::disconnect()
{
if (!AotfClose (hAotfController))
{
return DISCONNECTION_ERROR;
}
else
{
hAotfController = NULL;
return SUCCESSFUL;
}
}
AOTF_Controller::Error AOTF_Controller::init()
{
std::string ampCom="dds a 0 16383\r"; //Command to set the amplitude parameter to the max
std::string modCom="mod dac * 16383\r";//Command to set the dac parameter to the max
int gain = 255; // set the gain to the max
if (!AotfWrite(hAotfController, ampCom.length(), (char *)ampCom.c_str()))
{
return Error::INIT_ERROR;
}
if (!AotfWrite(hAotfController, modCom.length(), (char *)modCom.c_str()))
{
return INIT_ERROR;
}
setGain(gain);
return SUCCESSFUL;
}
AOTF_Controller::Error AOTF_Controller::reset()
{
std::string resetCom = "dds reset\r";
if(!AotfWrite(hAotfController, resetCom.length() , (char *)resetCom.c_str()))
{
return RESET_ERROR;
}
return SUCCESSFUL;
}
AOTF_Controller::Error AOTF_Controller::setFreq(float freq)
{
long freqHz = (long)freq*CONVERSION_MHZ_TO_HZ;
setFreq(freqHz);
return SUCCESSFUL;
}
AOTF_Controller::Error AOTF_Controller::setFreq(long freq)
{
std::ostringstream oss; //stream to build the string
//building the string for the Frequency
oss << "dds f 0 !" << std::fixed << std::setprecision(0) << freq << "\r";
std::string freqCom = oss.str();
//send command to the AOTF
if(!AotfWrite(hAotfController, freqCom.length(), (char *) freqCom.c_str())) // set the frequency (80-120)
{
return SET_ERROR;
}
currentFreq = freq; // update monitoring variable in HZ
return Error::SUCCESSFUL;
}
AOTF_Controller::Error AOTF_Controller::setGain(int gain)
{
std::ostringstream oss; //stream to build the string
//building the string for the Gain
oss << "dds gain -p* * " << gain << "\r";
std::string gainCom = oss.str();
//send command to the AOTF
if(!AotfWrite(hAotfController, gainCom.length(), (char * )gainCom.c_str())) // set the gain (0-255)
{
return SET_ERROR;
}
currentGain = gain;
return SUCCESSFUL;
}
AOTF_Controller::Error AOTF_Controller::setScanningFreq(float _startFreq, float _endFreq, int numOfFrames)
{
float FreqRange = (_endFreq - _startFreq); //calculate range
//calculate DeltaFrequency (frequency increase after each captured frame)
deltaFreq = (long) ((FreqRange/(float)(numOfFrames-1))*(float)CONVERSION_MHZ_TO_HZ); //conversion from MHz to Hz
startFreq = (long) (_startFreq*CONVERSION_MHZ_TO_HZ);
endFreq = (long) (_endFreq*CONVERSION_MHZ_TO_HZ);
setFreq(_startFreq);
return SUCCESSFUL;
}
AOTF_Controller::Error AOTF_Controller::increaseFreq()
{
if (deltaFreq ==0)
{
return SET_ERROR;
}
long newFreq = currentFreq + deltaFreq;
std::ostringstream oss;
oss << "dds f 0 !" << std::fixed << std::setprecision(0) << newFreq << "\r";
std::string freqCom = oss.str();
//send command to the AOTF
if(!AotfWrite(hAotfController, freqCom.length(), (char *)freqCom.c_str())) // set the frequency (80-120)value
{
return SET_ERROR;
}
currentFreq = newFreq;
return SUCCESSFUL;
}

Get the name of a monitor

I'm having a bit of trouble retrieving the name of a monitor with winapi. According to other entries on stackoverflow, the correct way to get the name of a monitor is this:
EnumDisplayDevices(nullptr, 0, &oDisplayDevice, 0);
char lpszDeviceName[32];
memcpy(lpszDeviceName, oDisplayDevice.DeviceName, 32);
EnumDisplayDevices(lpszDeviceName, 0, &oDisplayDevice, 0);
char lpszMonitorName[128];
memcpy(lpszMonitorName, oDisplayDevice.DeviceString, 128);
However, EnumDisplayDevices returns FALSE the second time around. The first time around, DeviceName is \\DISPLAY1 and DeviceString is the GPU vendor. Using the MONITORINFOEX struct gives me the same value as DeviceName.
To be clear I'm looking for something like "Samsung blah blah," or what appears in the control panel on the screen resolution page.
This seems to return the proper data for me:
#include <Windows.h>
#include <iostream>
#include <string>
int main()
{
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
int deviceIndex = 0;
while(EnumDisplayDevices(0, deviceIndex, &dd, 0))
{
std::string deviceName = dd.DeviceName;
int monitorIndex = 0;
while(EnumDisplayDevices(deviceName.c_str(), monitorIndex, &dd, 0))
{
std::cout << dd.DeviceName << ", " << dd.DeviceString << "\n";
++monitorIndex;
}
++deviceIndex;
}
return 0;
}
If you're compiling for UNICODE then use this instead:
#include <Windows.h>
#include <iostream>
#include <string>
int main()
{
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
int deviceIndex = 0;
while(EnumDisplayDevices(0, deviceIndex, &dd, 0))
{
std::wstring deviceName = dd.DeviceName;
int monitorIndex = 0;
while(EnumDisplayDevices(deviceName.c_str(), monitorIndex, &dd, 0))
{
std::wcout << dd.DeviceName << L", " << dd.DeviceString << L"\n";
++monitorIndex;
}
++deviceIndex;
}
return 0;
}
Here's an example of the output:
\.\DISPLAY1\Monitor0, Dell U2410(DP)\.\DISPLAY2\Monitor0, Dell
2407WFP-HC (Digital)
This worked for me on Win10. "Retired Ninja" answer returned Generic PnP monitor.
Don't forget to add DEFINES += "WINVER=0x0601" or define that
#include <windows.h>
void QueryDisplay()
{
std::vector<DISPLAYCONFIG_PATH_INFO> paths;
std::vector<DISPLAYCONFIG_MODE_INFO> modes;
UINT32 flags = QDC_ONLY_ACTIVE_PATHS;
LONG isError = ERROR_INSUFFICIENT_BUFFER;
UINT32 pathCount, modeCount;
isError = GetDisplayConfigBufferSizes(flags, &pathCount, &modeCount);
if( isError )
{
return;
}
// Allocate the path and mode arrays
paths.resize(pathCount);
modes.resize(modeCount);
// Get all active paths and their modes
isError = QueryDisplayConfig(flags, &pathCount, paths.data(), &modeCount, modes.data(), nullptr);
// The function may have returned fewer paths/modes than estimated
paths.resize(pathCount);
modes.resize(modeCount);
if ( isError )
{
return;
}
// For each active path
int len = paths.size();
for( int i=0 ; i<len ; i++ )
{
// Find the target (monitor) friendly name
DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
targetName.header.adapterId = paths[i].targetInfo.adapterId;
targetName.header.id = paths[i].targetInfo.id;
targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
targetName.header.size = sizeof(targetName);
isError = DisplayConfigGetDeviceInfo(&targetName.header);
if( isError )
{
return;
}
QString mon_name = "Unknown";
if( targetName.flags.friendlyNameFromEdid )
{
mon_name = QString::fromStdWString(
targetName.monitorFriendlyDeviceName);
}
qDebug() << "Monitor " << mon_name;
}
}

XercesC 2.7 failes to get a DOMWriter

I'm writing a server and I wanted to use XML with my Java client. I'm using CygWin with XercesC 3.1.1 for my development test and this works fine (I looped 30000 with this function and had no crash). However, on my target machine it's running HP-UX with XercesC 2.7. To implement the differences in the XercesC implementation I wrote a separate class to handle each version.
When I try to run the code with XercesC 2.7. I always get a NULL pointer when I try to create the DOMWriter, and a SIGABORT when trying again.
Since I couldn't find anyything on google I hope that someone can shed some light on what I'm doing wrong here. I've been looking at the sample code provided with the XercesC souorce, and I also have some production code from fellow programmers, and I can't see any difference for the live of it.
I tried to create an SSCE which is a bit long, but it is the shortest sample that I could create.
xml_serialize.h
#ifndef XML_SERIALIZE_H_
#define XML_SERIALIZE_H_
#include <string>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp>
#if defined(XERCES_NEW_IOSTREAMS)
#include <iostream>
#else
#include <iostream.h>
#endif
#include <xercesc/util/OutOfMemoryException.hpp>
class XStr
{
public :
// -----------------------------------------------------------------------
// Constructors and Destructor
// -----------------------------------------------------------------------
XStr(const char* const toTranscode)
{
// Call the private transcoding method
fUnicodeForm = xercesc::XMLString::transcode(toTranscode);
}
~XStr()
{
xercesc::XMLString::release(&fUnicodeForm);
}
// -----------------------------------------------------------------------
// Getter methods
// -----------------------------------------------------------------------
const XMLCh* unicodeForm() const
{
return fUnicodeForm;
}
private :
// -----------------------------------------------------------------------
// Private data members
//
// fUnicodeForm
// This is the Unicode XMLCh format of the string.
// -----------------------------------------------------------------------
XMLCh* fUnicodeForm;
};
#define X(str) XStr(str).unicodeForm()
std::string fromXMLString(XMLCh *oXMLString);
class XMLSerialize
{
private:
xercesc::DOMImplementation *mImpl;
protected:
xercesc::DOMImplementation *getDOMImplementation(void);
public:
XMLSerialize(void);
virtual ~XMLSerialize(void);
public:
/**
* Creates an empty DOM
*/
xercesc::DOMDocument *createDocument(const std::string &oDocumentName);
/**
* Parses an XML from a string.
*/
xercesc::DOMDocument *parseDocument(const std::string &oDocumentName, std::string const &oReferenceId);
/**
* Serializes the document into a string
*/
int serialize(xercesc::DOMDocument *oDocument, std::string &oXMLOut, bool bDocumentRelease = true);
};
#endif /* XML_SERIALIZE_H_ */
xml_serialize.cpp
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/TransService.hpp>
#include <xercesc/framework/MemBufFormatTarget.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
#include <sstream>
#include <vector>
#include <iostream>
#include "xml_serialize.h"
int serializeEnvironment(void);
XMLSerialize *serializer = NULL;
XMLSerialize::XMLSerialize()
{
mImpl = xercesc::DOMImplementationRegistry::getDOMImplementation(X("Core"));
}
XMLSerialize::~XMLSerialize()
{
}
xercesc::DOMDocument *XMLSerialize::createDocument(const std::string &oDocumentName)
{
if(mImpl == NULL)
return NULL;
xercesc::DOMDocument *doc = mImpl->createDocument(
0, // root element namespace URI.
X(oDocumentName.c_str()), // root element name
0); // document type object (DTD).
if(doc == NULL)
return NULL;
return doc;
}
int XMLSerialize::serialize(xercesc::DOMDocument *oDocument, std::string &oXMLOut, bool bDocumentRelease)
{
int result = 0;
XMLCh *xmlUnicode = NULL;
char *strXML = NULL;
xercesc::DOMWriter *serializer = NULL;
if(mImpl == NULL)
{
oXMLOut = "ERROR: XercesC DOMImplementationRegistry not initialized";
result = 1;
goto Quit;
}
serializer = ((xercesc::DOMImplementationLS*)mImpl)->createDOMWriter();
if(serializer == NULL)
{
oXMLOut = "ERROR: XercesC unable to instantiate a DOMWriter!";
result = 2;
goto Quit;
}
xmlUnicode = serializer->writeToString(*oDocument);
strXML = xercesc::XMLString::transcode(xmlUnicode);
oXMLOut = strXML;
if(bDocumentRelease == true)
oDocument->release();
result = 0;
Quit:
if(strXML != NULL)
xercesc::XMLString::release(&strXML);
if(xmlUnicode != NULL)
xercesc::XMLString::release(&xmlUnicode);
if(serializer != NULL)
serializer->release();
return result;
}
int serializeEnvironment(void)
{
int errorCode = 0;
xercesc::DOMElement *rootElem = NULL;
xercesc::DOMElement *item = NULL;
xercesc::DOMElement *element = NULL;
xercesc::DOMText *nameNode = NULL;
xercesc::DOMCDATASection *dataNode = NULL;
std::string xml;
try
{
xercesc::DOMDocument *doc = serializer->createDocument("EnvironmentList");
if(doc == NULL)
return 1;
rootElem = doc->getDocumentElement();
std::vector<std::pair<std::string, std::string> > env;
for(int i = 0; i < 5; i++)
{
std::string key;
std::string value;
std::stringstream ss;
ss << "KEY";
ss << i;
ss >> key;
ss.clear();
ss << "VALUE";
ss << i;
ss >> value;
ss.clear();
env.push_back(std::make_pair(key, value));
}
for(std::vector<std::pair<std::string, std::string> >::const_iterator it = env.begin(); it != env.end(); ++it)
{
std::pair<std::string, std::string>entry = *it;
std::string name = entry.first;
std::string value = entry.second;
if(value.empty())
value = "";
item = doc->createElement(X("item"));
rootElem->appendChild(item);
element = doc->createElement(X("item"));
nameNode = doc->createTextNode(X(name.c_str()));
item->appendChild(element);
element->appendChild(nameNode);
element = doc->createElement(X("item"));
dataNode = doc->createCDATASection(X(value.c_str()));
item->appendChild(element);
element->appendChild(dataNode);
}
errorCode = serializer->serialize(doc, xml);
std::cout << xml << std::endl;
doc->release();
errorCode = 0;
}
catch (const xercesc::OutOfMemoryException&)
{
XERCES_STD_QUALIFIER cerr << "OutOfMemoryException" << XERCES_STD_QUALIFIER endl;
errorCode = 2;
}
catch (const xercesc::DOMException& e)
{
XERCES_STD_QUALIFIER cerr << "DOMException code is: " << e.code << XERCES_STD_QUALIFIER endl;
errorCode = 3;
}
catch (...)
{
XERCES_STD_QUALIFIER cerr << "An error occurred creating the document" << XERCES_STD_QUALIFIER endl;
errorCode = 4;
}
return errorCode;
}
int main()
{
xercesc::XMLPlatformUtils::Initialize();
serializer = new XMLSerialize();
int error = 0;
for(int i = 0; i < 2; i++)
{
std::cout << "Serializing:" << i << " ... " << std::endl;
if((error = serializeEnvironment()) != 0)
std::cout << "ERROR" << error << std::endl;
std::cout << "Done" << std::endl;
}
xercesc::XMLPlatformUtils::Terminate();
return 0;
}
output
Serializing:0 ...
ERROR: XercesC unable to instantiate a DOMWriter!
Done
Serializing:1 ...
aCC runtime: pure virtual function called for class "xercesc_2_7::DOMImplementationLS".
Abort(coredump)
update
I finally managed to compile 2.7 for cygwin and tested the above code there. This works fine, so there must be some problem with the HP-UX environment.
I was compiling the code with gcc and aparently the xerces library was compiled with aCC. So nopw I switched to aCC in my makefile and now it works.
One should expect that the produced libraries are compatible, but apparently this is not the case. So the above code is actually correct.