We have a COM API for our application (which is written in VC++) which exposes a few functionalities so that the users can automate their tasks. Now, I'm required to add a new method in that, which should return a list/array/vector of strings. Since I'm new to COM, I was looking at the existing methods in the .idl file for that interface.
One of the existing methods in that idl file looks like this:
interface ITestApp : IDispatch
{
//other methods ..
//...
//...
//...
[id(110), helpstring("method GetFileName")] HRESULT GetFileName([out, retval] BSTR *pFileName);
//...
//...
//...
};
My task is to write a similar new method, but instead of returning one BSTR string, it should return a list/array/vector of them.
How can I do that?
Thanks!
Since yours is an automation-compatible interface, you need to use safearrays. Would go something like this:
// IDL definition
[id(42)]
HRESULT GetNames([out, retval] SAFEARRAY(BSTR)* names);
// C++ implementation
STDMETHODIMP MyCOMObject::GetNames(SAFEARRAY** names) {
if (!names) return E_POINTER;
SAFEARRAY* psa = SafeArrayCreateVector(VT_BSTR, 0, 2);
BSTR* content = NULL;
SafeArrayAccessData(psa, (void**)&content);
content[0] = SysAllocString(L"hello");
content[1] = SysAllocString(L"world");
SafeArrayUnaccessData(psa);
*names = psa;
return S_OK;
}
Error handling is left as an exercise for the reader.
Related
I am having a WCF (C# application) application that makes a call to a C++COM Dll. Now the method exposed by C++ COM dll has the following signature.
STDMETHODIMP MessageControl::CallMe(eEventType eventTypeVal, OUT long *pVal)
Now instead of returning a long *pVal I would like to return a interface which has multiple properties. Something like -
STDMETHODIMP MessageControl::CallMe(eEventType eventTypeVal, OUT IData *pVal)
So for the new interface how should I update the COM DLL? So Should I add a new entry in the IDL file for IData interface. I am new to this COM C++ Dll.
You need to have a function in your IDL like (assuming your interface is IMessageControl):
interface IMessageControl
{
STDMETHOD(CallMe)(eEventType eventTypeVal, [out,retval] IData** pVal);
}
or if you want option to return long or an object
interface IMessageControl
{
STDMETHOD(CallMe)(eEventType eventTypeVal, [out,retval] VARIANT* pVal);
}
Your C++ code would be something like:
STDMETHODIMP MessageControl::CallMe(eEventType eventTypeVal, IData** pVal)
{
return E_NOTIMPL;
}
or for variant version
STDMETHODIMP MessageControl::CallMe(eEventType eventTypeVal, VARIANT* pVal)
{
return E_NOTIMPL;
}
Obviously, once you implement, return S_OK for good return or an E_ type error code for a failure.
I make an OCX in C++ Builder Borland. My OCX has these function as I describe it:
Login
Write
Read
DebugOutput
GetVal
I am using this OCX in my c# application. In c# side I have a button and list box. The button calls login method from OCX and the list box shows this method's output.
In OCX side, the login method creates a command for server (with socket programming) to get authentication. Then call Write function to writes on socket. Then gets response from socket and calls Read function to reads the socket response.
The Read method reading the result and send it to DebugOutput to debug the output stream and call GetVal to find Last main server response. Then they pass their parameters to each other. after all the C# side show the result (SUCCESS | FAIL ) for login methods.
I used BSTR. (I read the topics about BSTR in stackoverflow and on MSDN but I think I did not get it well in my solution). These are my code in OCX side:
BSTR STDMETHODCALLTYPE TVImpl::Login(BSTR PassWord)
{
wchar_t wcs1[500];
Var *var=new Var();
//here make the command
...................
//get the server response to show to user
BSTR read=::SysAllocString(Write(wcs1));
if(read!=NULL) ::SysFreeString(read);
return read;
}
BSTR STDMETHODCALLTYPE TVImpl::Read()
{
BSTR str ;
try
{
IdTCPClient1->ReadTimeout=100;
str =::SysAllocString( IdTCPClient1->IOHandler->ReadLn().c_str());
}
catch(Exception &e)
{
str= e.Message.c_str();
}
str=(DebugOutput(str));
return str;
}
BSTR STDMETHODCALLTYPE TVImpl::Write(BSTR str)
{
IdTCPClient1->IOHandler->WriteLn(str) ;
BSTR str2=::SysAllocString(L"TST");
str2=Read();
return str2;
}
BSTR STDMETHODCALLTYPE TVImpl::GetVal(BSTR st,BSTR ValTyp)
{
BSTR res;
AnsiString stAnsi;
//Do some thing with st and save it to stAnsi
.................
res=(BSTR)WideString(stAnsi);
return ::SysAllocString(res);
}
BSTR STDMETHODCALLTYPE TVImpl::DebugOutput(BSTR st)
{
Var *val=new Var();
BSTR res;
res=GetVal(st,val->CMD_CMD);
if(res==val->CMD_AUTHENTICATE)
res=GetVal(st,val->XPassword);
return res;
}
In C3 code it was hanging. I know my problem is to use sysAllocString. but I when I used ::sysFreestring for each one in each method again my C# code hanging.
This is my C# code :
private void button4_Click(object sender, EventArgs e)
{
VinSockCmplt.Vin vin = new Vin();
listBox1.Items.Add(vin.Login("1234"));
}
You should not return BSTR (or any "complex" type that various compilers compile in various ways). The recommended return type for COM methods is HRESULT (a 32-bit integer). Also, you must not release a BSTR you just allocated and return it to the caller.
Here is how you could layout your methods:
HRESULT STDMETHODCALLTYPE TVImpl::Login(BSTR PassWord, /*out*/ BSTR *pRead)
{
...
*pRead = ::SysAllocString(L"blabla"); // allocate a BSTR
...
// don't SysFreeString here, the caller should do it
...
return S_OK;
}
If you were defining your interface in a .idl file, it would be something like:
interface IMyInterface : IUnknown
{
...
HRESULT Login([in] BSTR PassWord, [out, retval] BSTR *pRead);
...
}
retval is used to indicate assignment semantics are possible for languages that support it, like what you were trying to achieve initially with code like this var read = obj.Login("mypass")
There are lots of mistakes in your code:
if(read!=NULL) ::SysFreeString(read); return read; frees a string and then returns it
str= e.Message.c_str(); ... return str; returns a pointer to something that isn't even a BSTR
BSTR str2=::SysAllocString(L"TST"); str2=Read(); allocates a string and then leaks that string immediately
res=(BSTR)WideString(stAnsi); creates a dangling pointer
All of these are undefined behaviour (except the leak) and might cause a crash.
Also, I'm not sure if it is valid to have your functions return BSTR; the normal convention in COM programming is that functions return HRESULT and any "return values" go back via [out] parameters. This is compatible with all languages that have COM bindings; whereas actually returning BSTR limits your code compatibility. (I could be wrong - would welcome corrections here). You can see this technique used in the other question you linked.
To get help with a question like "Why is my code crashing?", see How to Ask and How to create a Minimal, Complete, and Verifiable example.
I have been poking around with WRL at the ABI layer for the last couple of weeks and have run into this problem.
I have an interface defined in IDL as follows:
namespace Async{
[uuid(f469e110-7ef5-41df-a237-9ddef9aed55c), version(1.0)]
interface IDownloader : IInspectable
{
HRESULT GetFeed([in] HSTRING url,[out, retval] Windows.Web.Syndication.SyndicationFeed ** feed);
[propget]HRESULT Feed([out, retval]Windows.Web.Syndication.SyndicationFeed ** feed);
}
[version(1.0), activatable(1.0)]
runtimeclass Downloader
{
[default] interface IDownloader;
}
}
Which I have defined in my header file like so:
#pragma once
#include "Async_h.h"
namespace ABI {
namespace Async {
class Downloader : public Microsoft::WRL::RuntimeClass<ABI::Async::IDownloader>
{
InspectableClass(L"Async.Downloader", BaseTrust);
public:
Downloader();
STDMETHOD(GetFeed)(HSTRING url, ABI::Windows::Web::Syndication::ISyndicationFeed ** feed);
STDMETHOD(get_Feed)(ABI::Windows::Web::Syndication::ISyndicationFeed ** feed);
private:
//Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Uri> feedUrl;
Microsoft::WRL::ComPtr<ABI::Windows::Web::Syndication::ISyndicationFeed> m_feed;
};
ActivatableClass(Downloader);
}
}
In my cpp file I implement the functions:
STDMETHODIMP Downloader::GetFeed(HSTRING url, ISyndicationFeed** feed)
{
HRESULT hr;
RoInitializeWrapper ro(RO_INIT_MULTITHREADED);
ComPtr<IUriRuntimeClass> uri;
ComPtr<IUriRuntimeClassFactory> uriFactory;
hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriFactory);
hr = uriFactory->CreateUri(url, uri.GetAddressOf());
ComPtr<ISyndicationClient> client;
ComPtr<IInspectable> inspectable;
RoActivateInstance(HStringReference(RuntimeClass_Windows_Web_Syndication_SyndicationClient).Get(), &inspectable);
hr = inspectable.As(&client);
Event timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
auto callback = Callback<IAsyncOperationWithProgressCompletedHandler<SyndicationFeed*,RetrievalProgress>>([&](IAsyncOperationWithProgress<SyndicationFeed*,RetrievalProgress> *op, AsyncStatus status) ->HRESULT
{
auto error = GetLastError();
if (status == AsyncStatus::Completed)
{
hr = op->GetResults(m_feed.GetAddressOf());
*feed = m_feed.Get();
}
return S_OK;
});
ComPtr<IAsyncOperationWithProgress<SyndicationFeed*,RetrievalProgress>> operation;
hr = client->RetrieveFeedAsync(uri.Get(), operation.GetAddressOf());
operation->put_Completed(callback.Get());
return S_OK;
}
STDMETHODIMP Downloader::get_Feed(ISyndicationFeed** feed)
{
*feed = m_feed.Get();
return S_OK;
}
The property works as expected it is projected to c++/cx as it should be. However,in the GetFeed method, when I attempt to set the feed parameter to the retrieved feed I get an access violation. Obviously I know that the memory is bad but the way I understand COM properties, they are essentially function calls and the property method and the GetFeed method are doing exactly the same thing minus the retrieval part.
Here are my questions:
What is the difference between COM property methods and regular interface methods in terms of the projected return value if any?
Why is the parameter to the property method initialized to nullptr and the parameter to the GetFeed Method not when they are described exactly the same in IDL?
If the out parameters in property methods are initialized, what part of the COM runtime is doing that for me and is that controllable? IE is there a way to get memory that I can write to passed to me?
I know that I could probably design that away but that is not the point. I am just trying to learn how it all works.
Thanks.
In your lambda you are capturing by reference with [&]. You need to capture the feed parameter by value, since the stack frame is long gone by the time your lambda executes.
The bigger issue is that the client has no idea when they can retrieve the results since you don't provide that information. (I see you create an unused Win32 Event object, so maybe there's some other code to make that work that you've deleted).
I'm trying to convert System::Windows::Forms::IDataObject into the native IDataObject defined in the ObjIdl.h. Unfortunately the following source code doesn't return the native IDataObject.
IntPtr pData = System::Runtime::InteropServices::Marshal::GetIUnknownForObject(e->Data);
::IDataObject* data = (::IDataObject*) pData.ToPointer();
How can I get the native IDataObject in a Windows Forms application during Drag and Drop?
Casting a COM interface pointer with a C-style cast is not legal. You have to ask nicely with the QueryInterface() method. Like this:
System::Windows::Forms::IDataObject^ obj = Clipboard::GetDataObject();
::IUnknown* punk = (::IUnknown*)System::Runtime::InteropServices::Marshal::GetIUnknownForObject(obj).ToPointer();
::IDataObject* pdata = nullptr;
HRESULT hr = punk->QueryInterface(__uuidof(::IDataObject), (void**)&pdata);
if (SUCCEEDED(hr)) {
// etc..
}
Using ATL (VS2008) how can I enumerate the available methods available on a given IDispatch interface (IDispatch*)? I need to search for a method with a specific name and, once I have the DISPID, invoke the method (I know the parameters the method takes.) Ideally I would like to do this using smart COM pointers (CComPtr<>).
Is this possible?
You can enumerate the methods an IDispatch exposes through the type info. There are two ways to get the type info:
through the type library (if any) for the dispinterface.
through calling IDispatch::GetTypeInfo.
Unfortunately, an IDispatch implementation is not obligated to provide type info about the methods and properties it implements.
If it does, however, the basic enumerating involves calling ITypeInfo::GetTypeAttr to get the TYPEATTR for the interface and looking at the number of implemented methods (cFuncs) and variables (cVars) and looping over these and calling ITypeInfo::GetFuncDesc() or ITypeInfo::GetVarDesc(). Of course, there are lot more details you will have to deal with as I can list here, but this should be a good starting point for your exploration.
Here's a nice article explaining the process in more details with code in VB.Net.
Here's some code that does the enumeration (it inserts the [Dispatch ID]-[Method Name] pairs in a map, but that's easy to change).
///
/// \brief Returns a map of [DispId, Method Name] for the passed-in IDispatch object
///
HRESULT COMTools::GetIDispatchMethods(_In_ IDispatch * pDisp,
_Out_ std::map<long, std::wstring> & methodsMap)
{
HRESULT hr = S_OK;
CComPtr<IDispatch> spDisp(pDisp);
if(!spDisp)
return E_INVALIDARG;
CComPtr<ITypeInfo> spTypeInfo;
hr = spDisp->GetTypeInfo(0, 0, &spTypeInfo);
if(SUCCEEDED(hr) && spTypeInfo)
{
TYPEATTR *pTatt = nullptr;
hr = spTypeInfo->GetTypeAttr(&pTatt);
if(SUCCEEDED(hr) && pTatt)
{
FUNCDESC * fd = nullptr;
for(int i = 0; i < pTatt->cFuncs; ++i)
{
hr = spTypeInfo->GetFuncDesc(i, &fd);
if(SUCCEEDED(hr) && fd)
{
CComBSTR funcName;
spTypeInfo->GetDocumentation(fd->memid, &funcName, nullptr, nullptr, nullptr);
if(funcName.Length()>0)
{
methodsMap[fd->memid] = funcName;
}
spTypeInfo->ReleaseFuncDesc(fd);
}
}
spTypeInfo->ReleaseTypeAttr(pTatt);
}
}
return hr;
}
You can't enumerate all the available methods unless the object implements IDispatchEx.
However, if you know the name of the method you want to call, you can use GetIDsOfNames to map the name to the proper DISPID.
HRESULT hr;
CComPtr<IDispatch> dispatch;
DISPID dispid;
WCHAR *member = "YOUR-FUNCTION-NAME-HERE";
DISPPARAMS* dispparams;
// Get your pointer to the IDispatch interface on the object here. Also setup your params in dispparams.
hr = dispatch->GetIDsOfNames(IID_NULL, &member, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
if (SUCCEEDED(hr)) {
hr = dispatch->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, dispparams, &varResult, NULL, NULL);
}
Edit: For completeness, I suspect there is a way to interrogate the ITypeInfo2 interface (assuming there is a type library for the object) that you get from IDispatch::GetTypeInfo for a list of methods, but I've not done it. See the other answer.