I have a c#.NET class having an Event signature which is exposed to COM using an outgoing interface like this::
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IHehServerEvents
{
string EmptyEvent(object sender, CustomEventArgs args);
}
In PluginAPI.h>>
I have
class PluginAPI: public IHehServerEvents,public FB::JSAPIAuto//implemented the COM outgoing interface
{
//declared EmptyEvent method based upon tlh file
HRESULT __stdcall EmptyEvent(VARIANT sender,IUnknown * args, BSTR * pRetVal);
FB_JSAPI_EVENT(CSharpEvent, 2, (FB::variant,IUnknown *));
}
HRESULT ClassCpluss::EmptyEvent(BSTR message, BSTR * pRetVal)
{....}
My Event helper in Firebreath statement is
FB_JSAPI_EVENT(CSharpEvent, 2, (VARIANT,struct _EventArgs *));
I also saw THIS link. SO boost::vairant is supported right?Then how to cast VARIANT to boost::variant?
Any changes expected in the above statement?
How to handle the above event in c# by use of VARIANT or struct?any examples would be helpful..
No, boost::variant is not supported, FB::variant is supported -- it is not the same thing at all. FB::variant is actually using boost::any internally.
What you're trying to do is mix COM and FireBreath and it's not going to have a good result. Instead, create a class that talks to your COM stuff and use that from your JSAPI object. Handle the event in your object and have it call a functor (look at boost::bind and boost::function) or maybe use signals and sockets from boost. There are lots of ways you can handle it, but the key here is that you need to first get your C++ object that knows how to talk to COM and then using that object from FireBreath will be easy. Directly using a VARIANT or arbitrary struct type with firebreath events will never work.
Related
I have to write a COM server DLL (using ATL) that is called by the plugin interface
of an old (closed-source) VB6 application and would like to avoid
possible leaks (of course)! The interface description is given as-is and cannot be
changed unfortunately:
The class method that will be called is declared on the VB side like this:
Public Function Process(data As Object,
Params As Variant,
Results() As Single) As Integer
The interface in IDL is declared like this:
[id(1)] HRESULT Process([in,out] IDispatch** pDataIDisp,
[in,out] VARIANT* pParamsVar,
[in,out] SAFEARRAY(FLOAT)* pResSA,
[out,retval] SHORT* pRetVal);
and finally the code that is called look like this:
STDMETHODIMP Analyzer::Process(IDispatch** pDataIDisp,
VARIANT* pParamsVar,
SAFEARRAY** pResSA,
SHORT* pRetVal)
{
try
{
// Prepare for access
CComPtr<IDispatch> dataComPtr = *pDataIDisp;
// VARTYPE from caller is VT_VARIANT | VT_ARRAY | VT_BYREF;
CComVariant prms = *pParamsVar; // OR use .Attach ?
CComSafeArray<VARIANT> prmsArr = prms.parray; // OR use prms.Attach ?
// SafeArray is FADF_HAVEVARTYPE
CComSafeArray<FLOAT> res = *pResSA; // OR use res.Attach(pResSA*) ?
{
// Use ATL types wrapped from above
}
// Cleanup ????
.
.
.
}
catch (...) {}
return S_OK;
}
What I'd like to know is:
Is my method of accepting (and converting) the parameters to ATL
types correct usage or is there another (better?) way?
Do I have to call AddRef() and/or Release() on the IDispatch* myself or
is it sufficient to assign to a CComPtr to get this all done?
What are the implications of the 2nd parameter given as VT_BYREF?
Header file says:
* VT_VARIANT [V][T][P][S] VARIANT *
* VT_ARRAY [V] SAFEARRAY*
* VT_BYREF [V] void* for local use
Which is not clear to me.... #-o
Is it sufficient to use SafeArray(Un)AccessData on the SAFEARRAY stored in
the VARIANT (2nd parameter)?
Any additional things to consider to get this thing work properly (robust)?
Thanks for taking your time and possibly helping me!
ps: I already got this working (more or less) I only want to avoid problems (LEAKS!)
which I cannot
debug as the calling application is closed source and not under my control...
You are making your like really compilcated using safe arrays and in/out parameters. Especially in conjunction with ancient VB6 you are going to interface to.
There are a few simple rules to make it clear, easier and reliable:
[in], [out], [out, retval] parameters; in your code snippet you used [in, out] without any shown intent to actually change the value to take advantage of out specifier: when you have no special reason to, it just makes C++ side complex and additional level of referencing increases chances for a mistake
in parameters don't need attaching to C++ classes and releasing
when you return from C++ method leaving a value for out parameter you typically Detach() interface pointers, strings, variants and safe arrays from holder C++ class - transferring ownership from internal class to obligation of caller to release the resources
instead of using safe arrays directly on IDL and trying to match signalture on VB side, I would suggest using variants: you can always put an array into variant, including array of variants, and VB6 will be able to roll this back
out and not retval parameters will be ByRef on VB6 side, in parameters will by ByVal
You would get it like this:
[id(1)] HRESULT Process([in] IDispatch* pDataIDisp,
[in] VARIANT pParamsVar,
[out, retval] VARIANT* pvResult);
On C++ server side you would only read from in arguments, no releasing required. And you will initialize output variant by building it entirely in C++ using ATL CComVariant and friends and then detaching it at the very last stage of your processing.
Public Function Process(data As Object,
Params As Variant) As Object
' ...
Dim Data, Params As Object
' ...
Dim Result As Object
Result = Server.Process(Data, Params)
' NOTE: Result is OK to be an array
Can I send a list of objects through COM?
I have my class and there is also a list of this class. And I need to send this through a COM function. Can I do that? If yes, then how? Do I need serialization marshaling?
COM does not pin down any particular collection type, it certainly doesn't have anything standard that models a list. By convention, you can model any collection with an interface. So say you have a collection of Foo objects that each implement IFoo. Then declare an IEnumFoo interface similar to:
interface IEnumFoo : IUnknown
{
HRESULT Next( [in] ULONG celt,
[out, size_is(celt), length_is(*pceltFetched)] IFoo **rgelt,
[in,out,unique] ULONG *pceltFetched );
HRESULT Skip( [in] ULONG celt);
HRESULT Reset();
HRESULT Clone( [out] IEnumFoo **ppenum );
}
And then simply return an interface pointer to an implementation of this interface to allow the client code to enumerate the list. Check the MSDN docs for IEnumVARIANT, a very common enumeration interface type for variants. Which also explains what the methods do.
If one side of the interface will be written in a language with a "simpler" type system, such as VB6 or script, then the SAFEARRAY is the way to go. COM's universal marshaller can take care of it.
If you have C++ on both sides of the interface, and you're happy to define the interface in IDL, and generate custom marshalling code, then IDL includes a "size_is" attribute that can be used. See here for details.
To any reasonable degree, there's no way to do so with std::list. I'm sure there is some maniac who could manage it but from my experience, it's not worth it. If doesn't have to be an std::list and just some linked list, then you'll have to either define a new COM interface and provide it's implementation (which can just be a wrapper around std::list) or find some thirdparty resource that has an adequate implementation for your purposes. If you don't have much experience with implementing COM interfaces, then you'll need to do some good amount of reading.
If it can be an array, then you can make your life a lot simpler by using a SAFEARRAY. And SAFEARRAYs can be easier to use with ATL's CComSafeArray, which is pretty much a wrapper. It tries to help/save you from issues, such as reference counting, when dealing with COM objects. You can then just pass the array between your COM objects. Just going to remind you that you still need to pay attention to who is responsible for de-allocating the memory.
There is one thing that bothers me, C++ function pointers. I'm asking this question because I'm trying to implement callback functions used in my game engine. The issue is, for example:
// Callback function
HRESULT RenderScene(float fps){ HRESULT hr; return S_OK; }
// Set the message
msk->SetMessage(0, SM_RENDERSCENE, (void*) RenderScene);
The problem is COM doesn't allow function pointers in their member functions. Also, doesn't allow for polymorphism. As you can see, I made it a void*. Fine with me because I know what the callback function is. The main issue is that I want something that is type-safe.
Now say if the user doesn't know the callback function declaration. For example,
// Callback function
HRESULT CALLBACK RenderScene() or RenderScene(int fps) or RenderScene(int a, int b)
Is implemented in WndProc as:
...
SM_RENDERSCENE:
((void (_stdcall*)(float fps)) pfn)(1.0f);
break;
...
The first, doesn't have an argument, therefore, he doesn't see fps. The second, loses precision. The third, loses precision and has an unused argument. Do you see where I'm going at? Tried using a union but COM doesn't allow function pointers in the member function.
I tried, and tried again. Nothing works, even the MFC message maps are ugly if they didnt have those macros.
To clarify, I rather have it return an error like E_FAIL or E_INVALIDFUNCTION if the function doesn't match the specification of SM_RENDERSCENE.
Does anyone have a solution to this problem.
Note: I like the COM specification and I'm not going to change, so focus on the issue not about why I'm using COM. Thank you, any help will do.
HRESULT RenderScene(float fps){ ...}
HRESULT CALLBACK RenderScene() or ...
Is implemented ... ((void (_stdcall*)(float fps)) pfn)(1.0f);
No, COM definitely supports function pointers. It is only when you use the subset of COM Automation or need to marshal function calls that you could get in trouble. Which is not the case here, you don't marshal between processes or threads and don't need automation since you work with only one language.
The simple problem is that you have the issue that you are trying to protect against, the function pointer definition doesn't match the implementation. Yes, a (void*) cast will stop the compiler from complaining about it, nothing good happens at runtime.
Your first declaration has the wrong calling convention. Using the STDMETHODIMP macro is wise.
Your second set of declarations have the wrong arguments.
The third snippet is applying an invalid function pointer cast, a cast to a function that returns void instead of HRESULT. And assumes __stdcall even though your RenderScene() function didn't use STDMETHODIMP or CALLBACK. Which is why you don't see a proper value for the argument.
Solve function pointer problems by declaring an alias for the pointer type:
typedef HRESULT (__stdcall * RenderSceneCallback)(float fps);
And consistenly use RenderSceneCallback in all your declarations. Never cast.
COM is a technology that is supposed to support multiple programming languages, and not all programming languages have support for all kind of features, hence some features like function pointers and exceptions are not (in general) supported in COM.
In the case of callbacks, usually the way to go is to define a COM interface that includes the method you need to call. You can implement this function in a COM class of your choice, and you can specify this interface as a parameter to any other COM function.
If you are using ATL for your COM implementation, here is a link on how to add an new interface to an ATL project. And you could also take a look on this article at codeproject about COM Event Handling
Pass interface pointers instead of function pointers. Also there is no function overloading in COM, so use different function names. Here is a pseudocode:
struct IRenderScene : public IUnknown
{
HRESULT RenderScene(); // Callback function
HRESULT RenderSceneFps(float fps); // Callback function
HRESULT RenderSceneAB(int a, int b); // Callback function
}
IRenderScene *renderScene = ...;
msk->SetRenderScene(renderScene); // Set the callback
You don't need to implement COM Event Handling (Connection Points) if it is superfluous to you.
I have to set the properties of a COM object.
The COM object has a Set function that takes a key (string) and a value (variant) as parameters. For example I can do : com_object->Set("name", "John").
I have many properties to set, but I don't know how to do it in C++. In VB.Net it would look like that:
com_object.Set "name,age", Array("M2", 1)
I would like to do the same in C++ but I don't know the syntax.
EDIT
Here is the code I have written to access properties in batch:
template < class ComObjectType >
void read(ComObjectType com_object, std::string const& fields)
{
COleSafeArray data_array_;
data_array_ = com_object->GetGet((LPCSTR)fields.c_str());
long index = 0;
VARIANT value_temp_;
_variant_t value_reader_;
data_array_.GetElement(&index, &value_temp_);
value_reader_.Attach(value_temp_);
std::string str1 = (LPCSTR)((_bstr_t) value_reader_)
++index
data_array_.GetElement(&index, &value_temp_);
value_reader_.Attach(value_temp_);
long long1 = value_reader_.lVal;
}
Then I call this function (which actually does not do much but is just there for the demonstration. The IAPIOrderObj* can be anything but it is defined in the .tlh file of the API I am being provided with, and it inherits from IDispatch interface.
MYAPI::IAPIOrderObj* my_obj;
read< IAPIOrderObj* >(my_obj, "StatusString,StatusCode");
What are you using to access the COM component MFC or ATL or only c++ library like _bsrt_t I cannot derive this information from your code snippet.
With some basic assumptions here are some points to consider.
Normally VB uses the IDispatch interface to access the COM components. From C++ there may be a native interface which may be much more easier to access than the IDispatch interface. From the com_object you can query this c++ interface.
Always use the COM strings (BSTR, SysAllocString allocated) for passing string information across.
Don't typecast from LPCSTR to _bstr_t. Both string representation has different meanings. Always use proper conversion functions to do the conversions.
Hope this helps.
I'm trying to call Visual Basic's CreateObject method from my C++ code. In VB, I'd simply type:
Dim obj As Object
obj = CreateObject("WScript.Network")
And that returns me the object from which I can call more methods. But how can I do that in C++? I'm following the MSDN documentation in http://msdn.microsoft.com/en-us/library/bb776046(v=VS.85).aspx, but those parameters are very obscure and I can't figure them out.
The first parameter is a reference to a CLSID, and I can see from the registry that the CLSID for "WScript.Network" is {093FF999-1EA0-4079-9525-9614C3504B74}. But what's the difference between this parameter and the third one, REFIID?
Thanks in advance!
I'll provide my solution, just for the record. It calls the AddWindowsPrinterConnection to install a network printer. It asks for user confirmation, so if you want to bypass that, you need to set HKEY_CURRENT_USER/Printers/LegacyPointAndPrint/DisableLegacyPointAndPrintAdminSecurityWarning to 1 (you can change it back to 0, after everything is done).
CoInitialize(NULL);
{
ATL::CComPtr<IDispatch> test;
_variant_t printerConnection = "\\\\serverAddress\\printerName";
_variant_t result;
test.CoCreateInstance(L"WScript.Network");
test.Invoke1(L"AddWindowsPrinterConnection", &printerConnection, &result);
}
CoUninitialize();
First, you probably want to use CoCreateInstance http://msdn.microsoft.com/en-us/library/ms686615%28VS.85%29.aspx, or the equivalent call inside a smart pointer wrapper (eg: CComPtr<>, _com_ptr<>, etc.).
Second, to your specific question, the IID is the interface ID, the CLSID is the class ID. COM objects can have multiple interfaces on the same object in general, which is why there is a distinction (although VB can only see one, which is why you don't need so to specify anything other than the CLSID for VB).
The "correct" way to duplicate what VB is doing is to create the IDispatch interface on the object, and then enumerate the methods using IDispatch. The "better" way in C++ is to create the direct interface you want to use, and call methods directly through it. However, this requires knowing the interface ID (IID, or REFIID passing the struct by reference), which is specific to the other object.
Hope that helps. I can't provide specifics for your particular interface, but maybe this points you in the right direction.