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.
Related
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.
The scenario is this:
I have a COM object to ask questions. Name it ICom.
The COM object requires me to implement an IDispatch , descendant of , say, IComEvents, that notifies me for events.
I implement an IDispatch and connect it to the COM interface.
So far so good. My IComEvents descentant's Invoke() is called when the events occur.
The point is now that I must manually parse Invoke() parameters. For example, if a notification function is HRESULT OnMouseHit(int x), I have to detect this function from the DispID, then call it manually, for example
if (dispIdMember == 0xfa)
{
OnMouseHit(pDispParams->rgvarg[0].pIntVal);
}
I would have to do it for all the functions I want to implement. However I saw the DispInvoke() function which presumably will automatically do this for me and call the appropriate overloaded method for the dispId, with the correct parameters:
DispInvoke(this,m_ptinfo,dispIdMember,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
How do I generate m_ptinfo? By calling LoadRegTypeLib with the libid of the COM object, and then ITypeLib::GetTypeInfoOfGuid() with the IID of IComEvents. However, DispInvoke() always returns "member not found".
What would be wrong? I expect DispInvoke to parse the type information, find the member function name from the DispID and then use the "this" pointer to get the function address from the vtbl.
What am I doing wrong?
Thanks a lot.
Michael.
Is IComEvents a dual interface or a dispinterface? If it is a pure dispinterface it doesn't have a vtable. DispInvoke requires the interface to have a vtable (ie that it is a dual interface).
I am using a C library, which uses callback functions.
Is there any way I can access calling object of C++ class ?
Edit:
I am using c-client lib.
Which have function mm_log.
void mm_log(char* string, long err_flag)
which is getting internally called by library. I want to check on which Imap stream it is getting called.
More Info
you can download library from ftp://ftp.cac.washington.edu/imap
All (good) C library functions that want a callback have a void* user_data pointer as part of the function and the callback parameter. You just pass a pointer to your object as this to the function and it just gets passed back to you in the callback. Example:
typedef void (*callback)(void*);
void dumb_api_call(callback cb, void* user_data){
cb(user_data);
}
struct Foo{};
void my_callback(void* my_data){
Foo* my_foo = static_cast<Foo*>(my_data);
}
int main(){
Foo my_foo;
dumb_api_call(my_callback, &my_foo);
}
If mm_log is a function which you are implementing and the library is calling (which is a terrible way for a library to do callbacks, by the way), then there is no way you can get it to reference a member function in your class.
What you could do is use a global variable which you set to point to your object before invoking the library (and clear after) and then use it within mm_log to invoke the desired method. This is nasty and dangerous but can work.
If you have more than one thread then exercise extreme caution - or find a better library.
Code is important for such a question. But without seeing any of your code, I can still give you a blanket statement :)
You'd have to wrap your C++ object with global functions that access a plain-old-struct, and export those with:
extern "C"
There are a plenty of caveats, but this is the gist of it.
See this FAQ: http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html
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.
I am not familiar with this, and can use a kick start.
I am using ATL (unmanaged C++) user control and would like to use the ShockWave ActiveX object. I need to know how to declare it so that I can set a property or call a method.
For instance, if I could assign a variable to it, then I would like to call 'variable->LoadMovie()'
I know this is super ridiculous... almost embarrassed to ask it here. ;) (almost)
If you #import the dll (which I recommend when working with COM because it makes your life SO much easier), you can use a smart pointer paired with the CLSID of the object. Remember that smart pointer classes have the post-fix 'Ptr' after the interface name.
For instance:
ISomeInterfacePtr pSomeInterface( CLSID_SomeComponent );
HRESULT hr = pSomeInterface->SomeMethod();
Hope that helps.
EDIT: If you want to check the HRESULT of the allocation, you can do the following:
ISomeInterfacePtr pSomeInterface = 0;
HRESULT hr = pSomeInterface.CreateInstance( CLSID_SomeComponent );
I cut&paste the necessary code so many times I can't remember the exact syntax but you have to:
get a CComPtr<> of the correct interface,
CreateInstance the object
QueryInterface to get the interface you want (assuming you're not using the CComPtr)
then call methods on it.
Alternatively you can #import the dll, then the compiler will generate a c++ class with all the methods and properties for you.