COM server as windows service, cannot pass array as function argument - c++

I'm implementing COM server (using ATL) as Windows service. I have the following method defined in service header:
STDMETHOD(SetBytes)(long lenSource, const BYTE* pSource, VARIANT_BOOL *pResult);
This method is declared in the IDL file:
[
object,
uuid(351C5A5F-3EB8-4CC5-AB79-6DCD27C2F7E0),
dual,
pointer_default(unique)
]
interface ISampleInterface: IUnknown {
HRESULT SetBytes([in] long lenSource, [in,ref,size_is(lenSource)] const BYTE* pSource, [out,retval] VARIANT_BOOL *pResult);
};
I'm calling it from my test application like this:
CoInitialize(NULL);
IUnknownPtr unknown_ptr;
HRESULT hr = unknown_ptr.CreateInstance(__uuidof(MyLib::SampleManager));
if (FAILED(hr)) {
...
};
MyLib::ISampleInterfacePtr sample_ptr;
sample_ptr = unknown_ptr; // no check here, assume sample_ptr is not null
VARIANT_BOOL function_result = VARIANT_FALSE;
vector<uint8_t> flash_data(1000, 2);
function_result = sample_ptr->SetBytes(flash_data.size(), &flash_data[0]);
I'm registering service by performing:
MyService.exe /regserver
MyService.exe -service
Then I'm executing test code step by step. When I'm going to tli file where we can see the following
HRESULT _hr = raw_SetBytes(lenSource, pSource, &_result);
pSource is absolutely ok and points to the area of memory where my data is contained. But when I'm going further (I'm attached to the service with debugger) and I'm in service's function SetBytes, only one byte from this array is contained in memory area and this pointer points to the different address.
I have tried implementing server via dll (it's registered in the system with regsvr32 [dllname]) and the pointer was absolutely ok in it in this case and all the length was passed, not only one byte.
I'm new to COM technology and wondering where I am wrong.

you could maybe wrap your BYTE array it into a SAFEARRAY.
STDMETHODIMP MyClass::getVariantFromCharArray( char *inputCharArray, UINT inputCharArrayLength, VARIANT *outputVariant)
{
SAFEARRAYBOUND saBound;
char *pData = NULL;
saBound.cElements = inputCharArrayLength;
saBound.lLbound = 0;
VariantInit( outputVariant);
(*outputVariant).vt = VT_UI1 | VT_ARRAY;
(*outputVariant).parray = SafeArrayCreate( VT_UI1, 1, &saBound);
if ( (*outputVariant).parray)
{
SafeArrayAccessData( (*outputVariant).parray, (void **)&pData);
memcpy( pData, inputCharArray, inputCharArrayLength);
SafeArrayUnaccessData( (*outputVariant).parray);
return S_OK;
}
else
{
return E_OUTOFMEMORY;
}
}

You need to use a SAFEARRAY to pass byte arrays around in COM.

Related

How to get an Instance of IWebBrowser2

I am a bit new to C++, please be gentle.
I am trying to automate Internet Explorer. I have a simple Win32 console application where I am trying to create an instance of IE using a local server.
However, my call to CoCreateInstance() doesn't return an object to initialize my IWebBrowser2 variable.
I could use some help to see what I am missing.
Here is my code:
HRESULT InstanciateIEResult;
HRESULT NavigateResult;
HRESULT ShowBrowserResult;
VARIANT * empty = new VARIANT();
BSTR URL = L"bing.com";
IWebBrowser2* pBrowser2;
InstanciateIEResult = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER,
IID_IWebBrowser2, (void**)&pBrowser2);
if(pBrowser2)
{
//never reach here
NavigateResult = pBrowser2->Navigate(URL, empty, empty, empty, empty);
ShowBrowserResult = pBrowser2->put_Visible(VARIANT_TRUE);
}
I am also not sure how to decode what the HRESULT returns. If you know, that would be helpful as well.
I was looking at documentation on IWebBrowser2 interface and CoCreateInstance.
You need to call CoInitialize() before using COM objects.
Also, you need to use SysAllocString() to allocate the string.
Example:
#include <windows.h>
#include <MsHTML.h>
#include <Exdisp.h>
#include <ExDispid.h>
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
CoInitialize(NULL);
HRESULT InstanciateIEResult;
HRESULT NavigateResult;
HRESULT ShowBrowserResult;
VARIANT empty;
VariantInit(&empty);
IWebBrowser2* browser = NULL;
HRESULT hr = CoCreateInstance(CLSID_InternetExplorer, NULL,
CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, (void**)&browser);
if (browser)
{
BSTR URL = SysAllocString(L"bing.com");
NavigateResult = browser->Navigate(URL, &empty, &empty, &empty, &empty);
SysFreeString(URL);
ShowBrowserResult = browser->put_Visible(VARIANT_TRUE);
browser->Release();
}
CoUninitialize();
return 0;
}

Problems understanding and using double pointers

I am not sure how to use double pointers.
The function i need to use looks as following:
HRESULT GetBuffer(
[out] IMediaSample **ppBuffer,
[in] REFERENCE_TIME *pStartTime,
[in] REFERENCE_TIME *pEndTime,
[in] DWORD dwFlags
);
Documentation says:
ppBuffer [out]
Receives a pointer to the buffer's IMediaSample interface. The caller must release the interface.
This is what i tried using it:
HRESULT MCMyOutputPin::Deliver(IMediaSample* sample)
{
HRESULT hr = NO_ERROR;
myLogger->LogDebug("In Outputpin Deliver", L"D:\\TEMP\\yc.log");
if (sample->GetActualDataLength() > 0)
{
IMediaSample **outsample;
m_pAllocator->GetBuffer(outsample, NULL, NULL, NULL); //Access violation here
BYTE** sampleBuffer;
BYTE** newBuffer;
sample->GetPointer(sampleBuffer);
(*outsample)->GetPointer(newBuffer);
memcpy((void *)newBuffer, (void *)sampleBuffer, sizeof(**sampleBuffer));
m_pInputPin->Receive(*outsample);
sample->AddRef();
}
return hr;
//Forward to filter
}
Which gives me an:
Access violation reading location 0xFFFFFFFFFFFFFFFF.
Then i tried using the address operator:
hr = m_pAllocator->GetBuffer(&outsample, NULL, NULL, NULL); //outsample is set to NULL
BYTE* sampleBuffer = NULL;
BYTE* newBuffer = NULL;
sample->GetPointer(&sampleBuffer);
outsample->GetPointer(&newBuffer);
memcpy((void *)newBuffer, (void *)sampleBuffer, sizeof(*sampleBuffer));
m_pInputPin->Receive(outsample);
This sets outsample to NULL.
So what is the correct syntax to handle double pointers?
My first, high-level comment, is that you are not checking the return values of the functions that you call. It's a mistake to neglect error checking. Your first step is to add the necessary error checking.
HRESULT GetBuffer(
[out] IMediaSample **ppBuffer,
[in] REFERENCE_TIME *pStartTime,
[in] REFERENCE_TIME *pEndTime,
[in] DWORD dwFlags
);
The first parameter is used to return a IMediaSample* to the caller. You need to declare a variable of type IMediaSample*, and pass its address:
IMediaSample* sample;
....
hr = m_pAllocator->GetBuffer(&outsample, ...);
// check hr
....
So, outsample is of type IMediaSample*. When you take its address, with &outsample, you now have something of type IMediaSample**, which is what you need.
Remember that when working with interfaces, you always work with a pointer to the interface.
You've made the same mistake with the BYTE** parameters. Again, declare variables of type BYTE*, and pass the address of these variables to the functions that you call.
BYTE* sampleBuffer;
BYTE* newBuffer;
....
hr = sample->GetPointer(&sampleBuffer);
// check hr
hr = outsample->GetPointer(newBuffer);
// check hr
Using sizeof(**sampleBuffer) in your call to memcpy is wrong. In your code, where sampleBuffer is wrongly declared as BYTE**, sizeof(**sampleBuffer) is just sizeof(BYTE) which is always 1.
In fact you can conclude that any use of sizeof is incorrect here because sizeof is evaluated at compile time. You need to find the actual size of the dynamic buffer at runtime using whatever functionality these interfaces provide.
The call to sample->AddRef() looks a little suspect. I don't see any evidence that anything has taken a new reference to the interface.
IMediaSample **outsample;
m_pAllocator->GetBuffer(outsample, NULL, NULL, NULL);
You're passing the function the value of outsample, which is a garbage value since it doesn't point to anything. You want:
IMediaSample *outsample;
m_pAllocator->GetBuffer(&outsample, NULL, NULL, NULL);
This gives the function the address of outsample so that it can stuff the pointer it wants to return in it.
I try to simulate the function as test, which using int** as output . the problem here is the function is suppose *output is is valid, so in the first way ,you have to make sure that, you might want to do something as this
sample = new IMediaSample[1]
this is my sample code, hope it helps
#include <iostream>
using namespace std;
void test(int** output ,int* in)
{
*output = new int[1];
output[0][0] = in[0];
}
int main()
{
int a[] ={ 2 };
int* out1;
test(&out1 ,a);
cout << out1[0] << endl;
int** out2;
out2 = new int*[1];
test(out2 ,a);
cout << out2[0][0] << endl;
return 0;
}

Return value from DLL function

I understand that the DLL typically has its own heap that it stores variables in. My DLL function takes a pointer to a wchar_t variable, and whenever I try to put a value in it, it simply exits the function, leaving the pointer pointing to a bad location which I'm assuming is because the heap gets destroyed.
If my DLL function comes up with some sort of data that needs to be passed back, could someone give me an example of how I could get that data in string format back to the original main function?
Using Visual Studio 2010.
Edit: I can provide some sample code, but I didn't see the point since I'm simply asking for an example/ explanation as to how memory is handled with regards to dll's and their functions. Ask me what information you need and I'll try to deliver.
Well, to give you guys an idea as to what the application does, it's a COM server DLL. The interface is IProperty, the object is called PropertyObject. The DLL was built separately, by me, with the PropertyObject methods. This method, Getproperty, is the one I'm working on.
STDMETHODIMP PropertyObject::GetProperty(int arg1, wchar_t* arg2)
{
arg2 = L"Test";
cout << *arg2 << endl;
return S_OK;
}
int main()
{
CoInitialize(NULL);
IClassFactory * pClassFactory = NULL;
HRESULT hr;
hr = CoGetClassObject(
CLSID_PropertyObject,
CLSCTX_SERVER,
NULL,
IID_IClassFactory,
(void **) &pClassFactory);
if (SUCCEEDED(hr))
{
wchar_t x = NULL;
IProperty *pProperty = NULL;
hr = pClassFactory->CreateInstance(NULL, IID_IProperty, (void **) &pProperty);
hr = pProperty->GetProperty(2, &x);
cout << x << endl;
}
return 0;
}
If you are 100% sure about the fact that all participating programs are compiled with the same version of Visual Studio (which implies they all use the same version of the STL that std::string is part of), you can use the std::string class.
If it needs to be interoperable, your best bet is passing in a char* and a length and write to that supplied buffer. Let the caller handle the memory. That's pretty C-style, but also your safest bet.
Turns out I was still considering a wchar_t pointer like a normal character array. here is my revised code:
STDMETHODIMP PropertyObject::GetProperty(int arg1, wchar_t* arg2)
{
wcscpy(arg2, L"Test"); // This is the function that i needed to be using.
return S_OK;
}
int main()
{
CoInitialize(NULL);
IClassFactory * pClassFactory = NULL;
HRESULT hr;
hr = CoGetClassObject(
CLSID_PropertyObject,
CLSCTX_SERVER,
NULL,
IID_IClassFactory,
(void **) &pClassFactory);
if (SUCCEEDED(hr))
{
wchar_t *x = new wchar_t; // Before, this was a normal variable. Changed it to a pointer.
IProperty *pProperty = NULL;
hr = pClassFactory->CreateInstance(NULL, IID_IProperty, (void **) &pProperty);
hr = pProperty->GetProperty(2, x); // Passed the pointer instead of an address to a normal variable.
wcout << x << endl; // wcout instead of cout. It worked.
}
return 0;
}

How to know if anybody has subscribed to a particular event I'm going to fire?

I got 2 events in my ActiveX/ATL library
dispinterface _IMyEvents
{
properties:
methods:
[id(1), helpstring("method OnReceiveData")] HRESULT OnReceiveData([in] long BytesReceived, [in, out] VARIANT_BOOL * Proceed);
[id(2), helpstring("method OnReceiveDataEx")] HRESULT OnReceiveDataEx([in] long BytesReceived, [in] BSTR DataChunk, [in, out] VARIANT_BOOL * Proceed);
};
They are called in the standard ATL's way:
template <class T>
class CProxy_IMyEvents : public IConnectionPointImpl<T, &DIID__IMyEvents, CComDynamicUnkArray>
{
//Warning this class may be recreated by the wizard.
public:
HRESULT Fire_OnReceiveData(LONG BytesReceived, VARIANT_BOOL * Proceed)
{
CComVariant varResult;
T* pT = static_cast<T*>(this);
int nConnectionIndex;
CComVariant* pvars = new CComVariant[2];
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
if (pDispatch != NULL)
{
VariantClear(&varResult);
pvars[1] = BytesReceived;
pvars[0].vt = VT_BYREF|VT_BOOL;
pvars[0].pboolVal = Proceed;
DISPPARAMS disp = { pvars, NULL, 2, 0 };
pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
}
}
delete[] pvars;
return varResult.scode;
}
I need to know if application has subscribed particularly to OnReceiveDataEx event (which has extra parameter DataChunk which needs to be calculated). If the app only listens to OnReceiveData, I don't need to build DataChunk string as nobody will get it, and can optimize the performance.
ATL, however, only allows me to know if anybody has subscribed to any events at all, but not to which ones particularly (so I can only determine the number of objects (sinks) listening to my events, but not the number and names of particular events being listened to). Is there any way to overcome this?
In .net, for instance, you can check subscribers for an event independently of other events.
If you need to make this distinction, ATL or no, you will need two interfaces. One for OnReceiveData and one for OnReceiveDataEx. A given event sink has to implement all the methods of an event interface, even if it only cares about one.

Returning COM object to JScript

I am trying to pass a COM object from an ActiveX component to JScript. So far I have tried the following method of doing so:
STDMETHODIMP CHSNetwork::CreateIPPPacket(VARIANT ** ppv)
{
IIPPacket *iipp;
HRESULT hr = CoCreateInstance(CLSID_IPPacket, NULL, CLSCTX_ALL, IID_IIPPacket, (void **)&iipp);
if(SUCCEEDED(hr) && ppv)
{
CComVariant cvar((IUnknown *)iipp);
hr = cvar.Detach(*ppv);
}
return hr;
}
The following JScript causes the error Variable uses an Automation type not supported in JScript:
var hsn = new ActiveXObject("ZIENetwork.HSNetwork");
var ipp = hsn.CreateIPPPacket();
Any help would be greatly appreciated. Thanks.
Ah. I got it. I needed to pass a VARIANT * not a VARIANT **. I guess I still get confused by pointers-to-pointers as it relates to return values with COM.
Thus the correct code is:
STDMETHODIMP CHSNetwork::CreateIPPPacket(VARIANT * ppv)
{
// TODO: Add your implementation code here
IIPPacket *iipp;
HRESULT hr = CoCreateInstance(CLSID_IPPacket, NULL, CLSCTX_ALL, IID_IIPPacket, (void **)&iipp);
if(ppv)
{
CComVariant cvar((IUnknown *)iipp);
hr = cvar.Detach(ppv);
}
return hr;
}
Of course if you only ever pass back an interface you can pass back a IDispatch** or IUnknown**, you don't need the VARIANT, just a thought :)