Formatting output text to .doc/.docx - c++

I searched how to formatting text to .doc/.docx file for weeks , but i don't found any good solution.
So my question is:
How to formatting text to output .doc/.docx file? (bold, center etc.)

Although your question is poorly formatted, I will do my best to help out.
Microsoft has a support page on using Microsoft Office formatting in C++. It looks like a pretty tough process.
The file you are concerned with is :
Word 2002 | msword.olb
Here is a codeguru link that attempts to address your issue, it might be somewhat out of date. It allows you to
CreateBlankDocument()
AppendText(CString szText)
GetLine(int nLine)
OpenWordFile(CString szFileName)
SaveWordFileAs(CString szFileName)
Okay : this Microsoft Word Automation post teaches you full functionality in automating a word document. My apologies for the wall of text to follow.
#include "stdafx.h"
#include <ole2.h>
//
// AutoWrap() - Automation helper function...
//
HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp,
LPOLESTR ptName, int cArgs...)
{
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);
if(!pDisp) {
MessageBox(NULL, "NULL IDispatch passed to AutoWrap()",
"Error", 0x10010);
_exit(0);
}
// Variables used...
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char buf[200];
char szName[200];
// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);
// Get DISPID for name passed...
hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT,
&dispID);
if(FAILED(hr)) {
sprintf(buf,
"IDispatch::GetIDsOfNames(\"%s\") failed w/err0x%08lx",
szName, hr);
MessageBox(NULL, buf, "AutoWrap()", 0x10010);
_exit(0);
return hr;
}
// Allocate memory for arguments...
VARIANT *pArgs = new VARIANT[cArgs+1];
// Extract arguments...
for(int i=0; i<cArgs; i++) {
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts!
if(autoType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}
// Make the call!
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType,
&dp, pvResult, NULL, NULL);
if(FAILED(hr)) {
sprintf(buf,
"IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx",
szName, dispID, hr);
MessageBox(NULL, buf, "AutoWrap()", 0x10010);
_exit(0);
return hr;
}
// End variable-argument section...
va_end(marker);
delete [] pArgs;
return hr;
}
int main(int argc, char* argv[])
{
// Initialize COM for this thread...
CoInitialize(NULL);
// Get CLSID for Word.Application...
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"Word.Application", &clsid);
if(FAILED(hr)) {
::MessageBox(NULL, "CLSIDFromProgID() failed", "Error",
0x10010);
return -1;
}
// Start Word and get IDispatch...
IDispatch *pWordApp;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
IID_IDispatch, (void **)&pWordApp);
if(FAILED(hr)) {
::MessageBox(NULL, "Word not registered properly",
"Error", 0x10010);
return -2;
}
// Make Word visible
{
VARIANT x;
x.vt = VT_I4;
x.lVal = 1;
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pWordApp, L"Visible", 1,
x);
}
// Get Documents collection
IDispatch *pDocs;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pWordApp, L"Documents",
0);
pDocs = result.pdispVal;
}
// Call Documents.Open() to open C:\Doc1.doc
IDispatch *pDoc;
{
VARIANT result;
VariantInit(&result);
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = ::SysAllocString(L"C:\\Doc1.doc");
AutoWrap(DISPATCH_METHOD, &result, pDocs, L"Open", 1, x);
pDoc = result.pdispVal;
SysFreeString(x.bstrVal);
}
// Get BuiltinDocumentProperties collection
IDispatch *pProps;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pDoc,
L"BuiltinDocumentProperties", 0);
pProps = result.pdispVal;
}
// Get "Subject" from BuiltInDocumentProperties.Item("Subject")
IDispatch *pPropSubject;
{
VARIANT result;
VariantInit(&result);
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = ::SysAllocString(L"Subject");
AutoWrap(DISPATCH_PROPERTYGET, &result, pProps, L"Item", 1, x);
pPropSubject = result.pdispVal;
SysFreeString(x.bstrVal);
}
// Get the Value of the Subject property and display it
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pPropSubject, L"Value",
0);
char buf[512];
wcstombs(buf, result.bstrVal, 512);
::MessageBox(NULL, buf, "Subject", 0x10000);
}
// Set the Value of the Subject DocumentProperty
{
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = ::SysAllocString(L"This is my subject");
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pPropSubject, L"Value", 1,
x);
::MessageBox(NULL,
"Subject property changed, examine document.",
"Subject", 0x10000);
SysFreeString(x.bstrVal);
}
// Get CustomDocumentProperties collection
IDispatch *pCustomProps;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pDoc,
L"CustomDocumentProperties", 0);
pCustomProps = result.pdispVal;
}
// Add a new property named "CurrentYear"
{
VARIANT parm1, parm2, parm3, parm4;
parm1.vt = VT_BSTR;
parm1.bstrVal = SysAllocString(L"CurrentYear");
parm2.vt = VT_BOOL;
parm2.boolVal = false;
parm3.vt = VT_I4;
parm3.lVal = 1; //msoPropertyTypeNumber = 1
parm4.vt = VT_I4;
parm4.lVal = 1999;
AutoWrap(DISPATCH_METHOD, NULL, pCustomProps, L"Add", 4, parm4,
parm3, parm2, parm1);
::MessageBox(NULL, "Custom property added, examine document.",
"Custom Property", 0x10000);
SysFreeString(parm1.bstrVal);
}
// Get the custom property "CurrentYear" and delete it
IDispatch *pCustomProp;
{
VARIANT result;
VariantInit(&result);
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = ::SysAllocString(L"CurrentYear");
AutoWrap(DISPATCH_PROPERTYGET, &result, pCustomProps, L"Item",
1, x);
pCustomProp = result.pdispVal;
SysFreeString(x.bstrVal);
AutoWrap(DISPATCH_METHOD, NULL, pCustomProp, L"Delete", 0);
::MessageBox(NULL,
"Custom property removed, examine document.",
"Custom Property", 0x10000);
}
// Close the document without saving changes and quit Word
{
VARIANT x;
x.vt = VT_BOOL;
x.boolVal = false;
AutoWrap(DISPATCH_METHOD, NULL, pDoc, L"Close", 1, x);
AutoWrap(DISPATCH_METHOD, NULL, pWordApp, L"Quit", 0);
}
// Cleanup
pCustomProp->Release();
pCustomProps->Release();
pPropSubject->Release();
pProps->Release();
pDoc->Release();
pDocs->Release();
pWordApp->Release();
// Uninitialize COM for this thread...
CoUninitialize();
return 0;
}

One of the possible solution is to use libOPC. Documentation shows the simple examples(not so simple). Its cross-platform solution.
One of the simplest example
#include <opc/opc.h>
int main( int argc, const char* argv[] )
{
opcInitLibrary();
opcContainer *c=opcContainerOpen(_X("sample.opc"), OPC_OPEN_READ_WRITE, NULL, NULL);
opcPart part1=opcPartCreate(c, _X("part1.xml"), _X("text/plain"), 0);
opcPart part2=opcPartCreate(c, _X("part2.xml"), _X("text/plain"), 0);
opcContainerOutputStream *stream1=opcContainerCreateOutputStream(c, part1);
opcContainerOutputStream *stream2=opcContainerCreateOutputStream(c, part2);
// WRITE to stream1 and stream2 concurrently using
opcContainerWriteOutputStream(stream1, "HELLO", 5);
opcContainerWriteOutputStream(stream2, "HELLO", 5);
opcContainerWriteOutputStream(stream2, " WORLD", 6);
opcContainerWriteOutputStream(stream1, " WORLD", 6);
opcContainerCloseOutputStream(stream1);
opcContainerCloseOutputStream(stream2);
opcContainerClose(c, OPC_CLOSE_NOW);
opcFreeLibrary();
return 0;
}

If you want to read or write .docx files you will want to familiarize yourself with the EMCA-376 Standard for Office Open XML format documents. You can find a collection of these documents (various editions) on the EMCA website

You'll need a library, or to do Word automation.
Word's internal format is quite complex, and you don't want to try to do this directly.

Related

Calling COM from C++ using CoCreateInstance and IDispatch, with numerous parameters in DISPPARAMS incl. SAFEARRAY

I have a COM object which exposes a function. I would like to pass parameters to this function and receive a return value. I'm using C++ with CoCreateInstance(). The error I receive is:
hr = 0x8002000e : Invalid number of parameters.
I'm reasonably sure that I have the correct number of parameters, which I can view in OleView:
[id(0x68030001), propget]
double My_function(
[in, out] double* PdblPrice,
[in, out] DATE* PdateStartDate,
[in, out] short* PintFlag,
[in, out] VARIANT_BOOL* PbolXP,
[in, out] SAFEARRAY(double)* PdblScale),
[out, retval] double*);
A summary of my code is as follows, and it works up to the point marked below:
#include <windows.h>
#include <objbase.h>
#include <comutil.h>
#include <vector>
#include <atlcomcli.h>
int main()
{
HRESULT hr;
// Create an instance of COM object
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CLSID clsid;
HRESULT nResult1 = CLSIDFromProgID(OLESTR("My_Library.clsMy_Library"), &clsid);
IUnknown* pUnknown;
hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IUnknown, (void**)&pUnknown);
// Get the IDispatch interface
IDispatch* pDispatch;
hr = pUnknown->QueryInterface(IID_IDispatch, (void**)&pDispatch);
// Call the Invoke method
DISPID dispid;
BSTR bstrFunction = SysAllocString(L"My_function");
hr = pDispatch->GetIDsOfNames(IID_NULL, &bstrFunction, 1, LOCALE_USER_DEFAULT, &dispid);
// ALL OF THE ABOVE WORKS.
// Prepare the arguments for the method call
// first convert a std::vector to SAFEARRAY
std::vector<double> _PdblScale = { 0, 0.25, 0.5, 0.75, 1.0, 0, 0, 0.5, 1, 1 };
SAFEARRAY* psa = SafeArrayCreateVector(VT_R8, 0, _PdblScale.size());
int* pData;
HRESULT hr_ = SafeArrayAccessData(psa, (void**)&pData);
if (SUCCEEDED(hr_))
{
for (unsigned int i = 0; i < _PdblScale.size(); i++)
{
pData[i] = _PdblScale[i];
}
SafeArrayUnaccessData(psa);
}
DISPPARAMS dispparams;
dispparams.cArgs = 5;
dispparams.rgvarg = new VARIANT[5];
dispparams.cNamedArgs = 5;
VARIANT PdblPrice;
PdblPrice.vt = VT_R8;
PdblPrice.dblVal = 28.0;
dispparams.rgvarg[0] = PdblPrice;
VARIANT PdateStartDate;
PdateStartDate.vt = VT_DATE;
PdateStartDate.date = 41052;
dispparams.rgvarg[1] = PdateStartDate;
VARIANT PintFlag;
PintFlag.vt = VT_I2;
PintFlag.iVal = 1;
dispparams.rgvarg[2] = PintFlag;
VARIANT PbolXP;
PbolXP.vt = VT_BOOL;
PbolXP.boolVal = false;
dispparams.rgvarg[3] = PbolXP;
VARIANT PdblScale;
PdblScale.vt = VT_SAFEARRAY;
PdblScale.pvRecord = psa;
dispparams.rgvarg[4] = PdblScale;
VARIANT varResult;
VariantInit(&varResult);
EXCEPINFO excepinfo;
memset(&excepinfo, 0, sizeof(excepinfo));
UINT nArgErr = (UINT)-1;
// Invoke the method ## THIS IS WHERE hr returns 0x8002000e : Invalid number of parameters
hr = pDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &varResult, &excepinfo, &nArgErr);
if (FAILED(hr))
{
printf("Failed to invoke method.");
pDispatch->Release();
pUnknown->Release();
CoUninitialize();
return 1;
}
// Print the result
printf("Result: %d\n", varResult.intVal);
// Clean up
VariantClear(&varResult);
pDispatch->Release();
pUnknown->Release();
CoUninitialize();
return 0;
}
I see from IDispatch Invoke() returns Type mismatch that the arguments should be in reverse order. I have tried that, ie. used [4], [3], [2], etc instead of [0], [1], etc above, but this still gives the error.
Any suggestions as to where I might be going wrong?
By the way, the COM is from a 32bit DLL, and I am compiling my code to x86.
There are many problems with your code:
lack of error handling.
when creating the COM object, you don't need to get its IUnknown just to immediately query it for IDispatch. You can get its IDispatch directly.
memory leaks on bstrFunction and dispparams.rgvarg.
you are creating a SAFEARRAY of VT_R8 (double) elements, but you are using an int* pointer to populate its values. You need to use a double* pointer instead.
you are not populating the DISPPARAMS or the VARIANTs correctly. The parameters have to be stored in the DISPPARAMS in revere order. And the VARIANTs need to use the VT_BYREF flag, which means they need to point at external variables that hold the actual values, rather than storing the values inside the VARIANTs themselves.
calling IDispatch::Invoke() with the wrong flag. You need to use DISPATCH_PROPERTYGET instead of DISPATCH_METHOD since the method is marked as propget in the IDL.
not using VARIANT_BOOL correctly for the bolXP parameter.
after invoking the method, you are printing out the wrong field of varResult. The method is declared as returning a double in the IDL, not an int.
With all of that said, try something more like this:
#include <windows.h>
#include <objbase.h>
#include <comutil.h>
#include <comdef.h>
#include <atlcomcli.h>
#include <vector>
// tweaked from https://devblogs.microsoft.com/oldnewthing/20040520-00/?p=39243
class CCoInitializeEx {
HRESULT m_hr;
public:
CCoInitializeEx() : m_hr(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)) { }
~CCoInitializeEx() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
operator HRESULT() const { return m_hr; }
};
int main()
{
HRESULT hr;
// Initialize COM
CCoInitializeEx init;
hr = init;
if (FAILED(hr)) {
printf("Failed to init COM.");
return -1;
}
// Create an instance of COM object and get its IDispatch interface
CLSID clsid;
hr = CLSIDFromProgID(OLESTR("My_Library.clsMy_Library"), &clsid);
if (FAILED(hr)) {
printf("Failed to get CLSID.");
return -1;
}
IDispatchPtr pDispatch;
hr = pDispatch.CreateInstance(clsid);
if (FAILED(hr)) {
printf("Failed to create COM object.");
return -1;
}
// Call the Invoke method
DISPID dispid;
_bstr_t bstrFunction = OLESTR("My_function");
LPOLESTR pbstrFunction = bstrFunction;
hr = pDispatch->GetIDsOfNames(IID_NULL, &pbstrFunction, 1, LOCALE_USER_DEFAULT, &dispid);
if (FAILED(hr)) {
printf("Failed to get DispID.");
return -1;
}
// ...
// first convert a std::vector to SAFEARRAY
// TODO: wrap the SAFEARRAY inside a RAII class...
std::vector<double> vecDblScale = { 0, 0.25, 0.5, 0.75, 1.0, 0, 0, 0.5, 1, 1 };
SAFEARRAY* psa = SafeArrayCreateVector(VT_R8, 0, vecDblScale.size());
if (!psa) {
printf("Failed to allocate SAFEARRAY.");
return -1;
}
double* pData;
hr = SafeArrayAccessData(psa, reinterpret_cast<void**>(&pData));
if (FAILED(hr))
printf("Failed to access SAFEARRAY data.");
SafeArrayDestroy(psa);
return -1;
}
for (size_t i = 0; i < vecDblScale.size(); ++i)
{
pData[i] = vecDblScale[i];
}
// alternatively:
//
// #include <algorithm>
// std::copy(vecDblScale.begin(), vecDblScale.end(), pData);
SafeArrayUnaccessData(psa);
// Prepare the arguments for the method call
std::vector<VARIANT> vecRgvarg(5);
DOUBLE dblPrice = 28.0;
vecRgvarg[4].vt = VT_R8 | VT_BYREF;
vecRgvarg[4].pdblVal = &dblPrice;
DATE dateStartDate = 41052;
vecRgvarg[3].vt = VT_DATE | VT_BYREF;
vecRgvarg[3].pdate = &dateStartDate;
short intFlag = 1;
vecRgvarg[2].vt = VT_I2 | VT_BYREF;
vecRgvarg[2].piVal = &intFlag;
VARIANT_BOOL bolXP = VARIANT_FALSE;
vecRgvarg[1].vt = VT_BOOL | VT_BYREF;
vecRgvarg[1].pboolVal = &bolXP;
vecRgvarg[0].vt = VT_R8 | VT_ARRAY | VT_BYREF;
vecRgvarg[0].pparray = &psa;
DISPPARAMS dispparams = {};
dispparams.cArgs = vecRgvarg.size();
dispparams.rgvarg = vecRgvarg.data();
// Invoke the method
_variant_t varResult;
EXCEPINFO excepinfo = {};
UINT nArgErr = (UINT)-1;
hr = pDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, &varResult, &excepinfo, &nArgErr);
if (FAILED(hr)) {
printf("Failed to invoke method.");
SafeArrayDestroy(psa);
return -1;
}
// Print the result
printf("Result: %f\n", varResult.dblVal);
// Clean up
SafeArrayDestroy(psa);
return 0;
}

0x80020009 OLE Exception accessing any number other than 0

So I'm using COM to access a third party program, I use the following Method to acquire property from server:
HRESULT ConnectToHYSYS::OLEMethod(int nType, VARIANT * pvResult, IDispatch * pDisp, LPOLESTR ptName, int cArgs...){
if (!pDisp) return E_FAIL;
va_list marker;
va_start(marker, cArgs);
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
char szName[200];
// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);
// Get DISPID for name passed...
HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
if (FAILED(hr)) {
return hr;
}
// Allocate memory for arguments...
VARIANT * pArgs = new VARIANT[cArgs + 1];
// Extract arguments...
for (int i = 0; i < cArgs; i++) {
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts!
if (nType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}
// Make the call!
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, nType, &dp, pvResult, NULL, NULL);
if (FAILED(hr)) {
return hr;
}
// End variable-argument section...
va_end(marker);
delete[] pArgs;
return hr;
}
When I try to access an object in Collection through the following Method:
void ConnectToHYSYS::GetMaterialStream(int index) {
HRESULT hr;
VARIANT result;
VariantInit(&result);
hr = OLEMethod(DISPATCH_PROPERTYGET, &result, hyStreams, L"Item", 1, index);
CheckForHr(hr);
hyStream = result.pdispVal;}
It returns the hyStream member only if index=0; I checked the following:
1- hyStreams (the collection) pointer is not NULL
2- The member with index Im trying to access exists (The collection has 36 hyStreams but fails to get any hyStream with index more than 0)
3- When Debugging, the error happens when I make the Call
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, nType, &dp, pvResult, NULL, NULL);
Where hr returns above mentioned error...
Can you figure out where is the problem?
Thank you
The problem was in the type that was passed (index type), I think all kinds of types going through COM has to b kind of object.. anyways, changing it to _variant_t and including (without it, u get nasty errors of linking and stuff), the problem was solved and I could loop through the whole collection.

How to properly use CComPtr in function calls?

I'm new to COM and smartpointers, I'm trying to convert a project from raw pointers to CComPtr to avoid the hassle with memory management. I'm looking for some advice on how to properly use CComPointers when it comes to functions and scope in general. A sample of my code.
int DisplayDeviceInformation(IEnumMoniker * pEnum, IMoniker * pMoniker)
{
CComPtr<IPropertyBag> pPropBag = NULL;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
{
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
if (FAILED(hr))
{
}
VARIANT var;
VariantInit(&var);
// Get description or friendly name.
hr = pPropBag->Read(L"Description", &var, 0);
if (FAILED(hr))
{
hr = pPropBag->Read(L"FriendlyName", &var, 0);
}
if (SUCCEEDED(hr))
{
printf("%S\n", var.bstrVal);
VariantClear(&var);
}
hr = pPropBag->Write(L"FriendlyName", &var);
// WaveInID applies only to audio capture devices.
hr = pPropBag->Read(L"WaveInID", &var, 0);
if (SUCCEEDED(hr))
{
printf("WaveIn ID: %d\n", var.lVal);
VariantClear(&var);
}
hr = pPropBag->Read(L"DevicePath", &var, 0);
if (SUCCEEDED(hr))
{
// The device path is not intended for display.
printf("Device path: %S\n", var.bstrVal);
VariantClear(&var);
}
}
return 0;
}
CComPtr<IMoniker> pMoniker = NULL;
CComPtr<IMoniker> pMoniker2 = NULL;
CComPtr<IEnumMoniker> pEnum = NULL;
hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
//pEnum->Next(1, &pMoniker,&cFetched);
if (SUCCEEDED(hr))
{
DisplayDeviceInformation(pEnum, pMoniker);
}
pEnum = NULL;
hr = EnumerateDevices(CLSID_AudioInputDeviceCategory, &pEnum);
//pEnum->Next(1, &pMoniker2,&cFetched);
if (SUCCEEDED(hr))
{
DisplayDeviceInformation(pEnum, pMoniker);
}
Basiclly, the first DisplayDeviceInformation(pEnum, pMoniker); gives a p==0 error. If I however uncomment the pEnum->Next(1, &pMoniker,&cFetched); it works. With raw pointers I don't have to do that since the code just skips to the next device. Any advice or help would make me outmost grateful, thanks in advance!
Where CComPtr gives you an assert failure, you are likely to have an issue with raw pointers as well. You just are not pre-warned and the problem comes up later, e.g. as a reference leak.
You don't seem to need IMoniker in global scope
You need to clear IMoniker pointer before supplying it for the enumerator
See below:
int DisplayDeviceInformation(IEnumMoniker* pEnum, IMoniker** ppSelectedMoniker)
{
CComPtr<IMoniker> pMoniker;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
{
CComPtr<IPropertyBag> pPropBag; // You need it clear to start from for every moniker
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
// ...
if(we should stop enumeration and we are good with current moniker)
{
//ATLASSERT(ppSelectedMoniker != NULL);
*ppSelectedMoniker = pMoniker.Detach();
return ...
}
// ...
pMoniker.Release(); // You have to do this, so that next Next would accept empty CComPtr as an argument
}
return 0;
}
CComPtr<IEnumMoniker> pEnumVideoMoniker;
CComPtr<IMoniker> pSelectedVideoMoniker;
hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnumVideoMoniker);
if (SUCCEEDED(hr))
DisplayDeviceInformation(pEnumVideoMoniker, &pSelectedVideoMoniker);
CComPtr<IEnumMoniker> pEnumAudioMoniker;
CComPtr<IMoniker> pSelectedAudioMoniker;
hr = EnumerateDevices(CLSID_AudioInputDeviceCategory, &pEnumAudioMoniker);
if (SUCCEEDED(hr))
DisplayDeviceInformation(pEnumAudioMoniker, &pSelectedAudioMoniker);

Need to retrieve all groups a user belongs to... in C++

I need to find all the groups a particular user is a member of. I'm using C++, not Powershell, if this is the wrong forum I apologize.
From what I've found on the web I need to retrieve the memberOf property, but I get an error that the property doesn't exist. Any help would be appreciated. Here's the code:
HRESULT hrObj = E_FAIL;
HRESULT hr = E_FAIL;
ADS_SEARCHPREF_INFO SearchPrefs;
// COL for iterations
ADS_SEARCH_COLUMN col;
// Handle used for searching
ADS_SEARCH_HANDLE hSearch;
// Search entire subtree from root.
SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
SearchPrefs.vValue.Integer = ADS_SCOPE_SUBTREE;
// Set the search preference.
DWORD dwNumPrefs = 1;
hr = pSearchBase->SetSearchPreference(&SearchPrefs, dwNumPrefs);
if (FAILED(hr))
{
return hr;
}
// Create search filter.
LPWSTR pszFormat = L"(&(objectCategory=person)(objectClass=user)(sAMAccountName=%s))";
int len = wcslen(pszFormat) + wcslen(szFindUser) + 1;
LPWSTR pszSearchFilter = new WCHAR[len];
if(NULL == pszSearchFilter)
{
return E_OUTOFMEMORY;
}
swprintf_s(pszSearchFilter, len, pszFormat, szFindUser);
// Set attributes to return.
LPWSTR pszAttribute[NUM_ATTRIBUTES] = {L"ADsPath"};
// Execute the search.
hr = pSearchBase->ExecuteSearch(pszSearchFilter,
pszAttribute,
NUM_ATTRIBUTES,
&hSearch);
if (SUCCEEDED(hr))
{
// Call IDirectorySearch::GetNextRow() to retrieve the next row of data.
while(pSearchBase->GetNextRow(hSearch) != S_ADS_NOMORE_ROWS)
{
// Loop through the array of passed column names and
// print the data for each column.
for (DWORD x = 0; x < NUM_ATTRIBUTES; x++)
{
// Get the data for this column.
hr = pSearchBase->GetColumn(hSearch, pszAttribute[x], &col);
if (SUCCEEDED(hr))
{
// Print the data for the column and free the column.
// Be aware that the requested attribute is type CaseIgnoreString.
if (ADSTYPE_CASE_IGNORE_STRING == col.dwADsType)
{
IADs *pADS;
hr = ADsOpenObject( col.pADsValues->CaseIgnoreString,
L"Administrator",
L"passW0rd",
ADS_SECURE_AUTHENTICATION,
IID_IADs,
(void**)&pADS);
VARIANT var;
VariantInit(&var);
if (SUCCEEDED(hr))
{
hr = pADS->GetEx(L"memberOf", &var); <-- FAILS!!!
wprintf(L"Found User.\n",szFindUser);
wprintf(L"%s: %s\r\n",pszAttribute[x],col.pADsValues->CaseIgnoreString);
hrObj = S_OK;
}
}
pSearchBase->FreeColumn( &col );
}
else
{
hr = E_FAIL;
}
}
}
// Close the search handle to cleanup.
pSearchBase->CloseSearchHandle(hSearch);
}
delete pszSearchFilter;
if (FAILED(hrObj))
{
hr = hrObj;
}
Unless you're set on using AD directly, it's probably easier to use the Windows Net* functions for the job:
#include <windows.h>
#include <lm.h>
#include <stdio.h>
int main() {
wchar_t user[256];
DWORD size = sizeof(user)/sizeof(user[0]);
GetUserNameW(user, &size);
printf("User: %S\n", user);
printf("Local groups: \n");
LPBYTE buffer;
DWORD entries, total_entries;
NetUserGetLocalGroups(NULL, user, 0, LG_INCLUDE_INDIRECT, &buffer, MAX_PREFERRED_LENGTH, &entries, &total_entries);
LOCALGROUP_USERS_INFO_0 *groups = (LOCALGROUP_USERS_INFO_0*)buffer;
for (int i=0; i<entries; i++)
printf("\t%S\n", groups[i].lgrui0_name);
NetApiBufferFree(buffer);
printf("Global groups: \n");
NetUserGetGroups(NULL, user, 0, &buffer, MAX_PREFERRED_LENGTH, &entries, &total_entries);
GROUP_USERS_INFO_0 *ggroups = (GROUP_USERS_INFO_0*)buffer;
for (int i=0; i<entries; i++)
printf("\t%S\n", ggroups[i].grui0_name);
NetApiBufferFree(buffer);
return 0;
}
Thanks for the reply, I think I found what I was looking for in MSDN.
HRESULT CheckUserGroups(IADsUser *pUser)
{
IADsMembers *pGroups;
HRESULT hr = S_OK;
hr = pUser->Groups(&pGroups);
pUser->Release();
if (FAILED(hr)) return hr;
IUnknown *pUnk;
hr = pGroups->get__NewEnum(&pUnk);
if (FAILED(hr)) return hr;
pGroups->Release();
IEnumVARIANT *pEnum;
hr = pUnk->QueryInterface(IID_IEnumVARIANT,(void**)&pEnum);
if (FAILED(hr)) return hr;
pUnk->Release();
// Enumerate.
BSTR bstr;
VARIANT var;
IADs *pADs;
ULONG lFetch;
IDispatch *pDisp;
VariantInit(&var);
hr = pEnum->Next(1, &var, &lFetch);
while(hr == S_OK)
{
if (lFetch == 1)
{
pDisp = V_DISPATCH(&var);
pDisp->QueryInterface(IID_IADs, (void**)&pADs);
pADs->get_Name(&bstr);
printf("Group belonged: %S\n",bstr);
SysFreeString(bstr);
pADs->Release();
}
VariantClear(&var);
pDisp=NULL;
hr = pEnum->Next(1, &var, &lFetch);
};
hr = pEnum->Release();
return S_OK;
}

How can I remove a MS Word add-in button?

I need to programmatically remove an add-in from MS Word. I have deleted the registry entry corresponding to it, and the button is now disabled (nothing happens when you click it) and the add-in no longer appears on the list of COM Add-ins.
The button, however, remains in the Add-ins ribbon menu. How can I remove that programmatically?
No answers after a week. You can tell its a lazy question, can't you?
I am currently using a solution from CodeProject. My code seems to work, but it has not been tested properly yet.
CoInitialize(NULL);
CLSID clsid;
IDispatch *pWApp, *pCommandBars, *pCommandBar, *pCommandBarControls, *pCommandBarControl;
VARIANT v;
HRESULT hr;
hr = CLSIDFromProgID(L"Word.Application", &clsid);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pWApp);
if (SUCCEEDED(hr))
{
VariantInit(&v);
hr = OLEMethod(DISPATCH_PROPERTYGET, &v, pWApp, L"Commandbars", 0);
pCommandBars = v.pdispVal;
VariantInit(&v);
hr = OLEMethod(DISPATCH_PROPERTYGET, &v, pCommandBars, L"Count", 0);
INT NumCommBars = v.intVal;
for (INT i = 1; i <= NumCommBars; i++)
{
COleVariant CommandBarIndex((BYTE)i);
VariantInit(&v);
hr = OLEMethod(DISPATCH_PROPERTYGET, &v, pCommandBars, L"Item", 1, CommandBarIndex);
if (FAILED(hr)) continue;
pCommandBar = v.pdispVal;
VariantInit(&v);
hr = OLEMethod(DISPATCH_PROPERTYGET, &v, pCommandBar, L"Controls", 0);
if (FAILED(hr)) continue;
pCommandBarControls = v.pdispVal;
VariantInit(&v);
hr = OLEMethod(DISPATCH_PROPERTYGET, &v, pCommandBarControls, L"Count", 0);
if (FAILED(hr)) continue;
INT NumControls = v.intVal;
for (INT j = 1; j <= NumControls; j++)
{
COleVariant CommandBarControlIndex((BYTE)j);
VariantInit(&v);
hr = OLEMethod(DISPATCH_PROPERTYGET, &v, pCommandBarControls, L"Item", 1, CommandBarControlIndex);
if (FAILED(hr)) continue;
pCommandBarControl = v.pdispVal;
VariantInit(&v);
hr = OLEMethod(DISPATCH_PROPERTYGET, &v, pCommandBarControl, L"Caption", 0);
if (FAILED(hr)) continue;
CString ControlCaption(v.bstrVal);
if (ControlCaption.Find("MyCaption") != -1)
{
OLEMethod(DISPATCH_METHOD, NULL, pCommandBarControl, L"Delete", 0);
}
pCommandBarControl->Release();
}
pCommandBar->Release();
pCommandBarControls->Release();
}
OLEMethod(DISPATCH_METHOD, NULL, pWApp, L"Quit", 0);
pCommandBars->Release();
pWApp->Release();
}
}
CoUninitialize();
Hope this helps others with a similar problem.