views:

136

answers:

5

I have code that looks like this:

extern "C" __declspec(dllexport)  myInterface(int id, void** pFunction)
{
   ...
}

I need to make the void** pFunction argument point to a function so that the caller can use this function via the pFunction pointer. This function gets called through a DLL, I don't want to do it this way but for a lot of reasons I have no choice. I know that COM is made for this but I can not use it, the reasons come down to management.

At this point I have no idea how to do this, everything I have tried to do gives me cast problems. Do anyone have any idea how I can do this? I can post more if this is unclear.

Thanks.

+1  A: 

This is not something that can be done in standard C or C++. There is no guarantee that a function pointer can fit into a void pointer (C++ member function pointers typically can't). In other words, if you can't change the function signature, you can't do what you want in standard C or C++, and there's no guarantee you can do it at all.

Therefore, any solution would be a platform-specific one. You don't specify a platform directly in question or tag, but my guess would be Visual C++ from other things.

Please specify your platform specifically, and anything useful about the function pointer you want to pass.

David Thornley
The platform is visual c++ yes, but this is a cross-platform application. It runs in the NUT_C enviornment so we can run Linux on a Windows operating system. The compiler is the visual c++ 6.0 one but it is run through CXX. Thats my understanding, I am new to this company so I am learning as I go. Basically I have no choice but to make the void** work, I have read the same documentaion on what you have said but I need to pull this off, I am basically cheating or faking COM here.
Dixon Steel
What I mean by run Linux, is, we run a Linux app on Windows via the NUT_C cshell.
Dixon Steel
@Dixon Steel: Unfortunately, some things are just impossible, and others are impossible to do in a platform-independent way, or with certain management constraints. That being said, I'm not by any means an expert with VC++ 6.0, and I don't even know what NUT_C is or what you mean by CXX. Now that you've given your platform(s), people can suggest platform-dependent solutions.
David Thornley
+3  A: 

If you are looking at the implementation of 'myInterface', then you might be wanting:

switch (id)
{
case FUNC_1:
    *pFunction = (void *)first_function;
    break;
...
}

If you are trying to call the function and pass in a pointer to function, then:

void *vp = (void *)the_function_to_pass;
myInterface(1, &vp);

If you have something else in mind, you need to specify what.

(Note that strictly, C does not guarantee that function pointers can be assigned to object pointers and vice versa. However, POSIX does make that guarantee for you. I believe similar comments apply to C++.)

Jonathan Leffler
What I have tried is similar to the switch statement, but when I do the cast on the function the compiler says it's not possible, but that is the idea of what I want to do. So when the caller gets the poiter it can use that to run the function from the DLL.
Dixon Steel
+1 for the note that function pointers and object pointers are not necessarily compatible in C (and that POSIX says "shut up, C!")
Jonathan Grynspan
@Dixon: Note the absence of parentheses after the name of the function...one possible cause of the compiler saying 'no can do' is that you are invoking the function instead of naming it. Otherwise, we'd need to see the exact error from the compiler. Note that in C++, pointers to member functions are not the same as pointers to functions - you cannot use this technique to play with member functions.
Jonathan Leffler
This way makes the compiler happy but at run time the windbg reports that the type information is missing and the function never gets called.
Dixon Steel
@Dixon: the code I showed didn't cover "invoking the function after you have stored it in the void pointer". You would have to do another cast back to the correct 'pointer to function' before invoking it. I'm assuming you have it stored in a 'void *' (rather than 'void **') when you need to invoke it. Then you need to do something like: `int (*function)(double, char *) = vp; function(3.14159, "abc");`, where you might need a (reinterpret) cast to force the assignment to work. You can write it in one: `(*(int (*)(double, char *))vp)(3.14159, "abc");` - but that may be more inscrutable to you.
Jonathan Leffler
@Dixon: if the casting process does not work either, then I don't understand the Windows environment at all - whereas I though I had a bad understanding of it.
Jonathan Leffler
+1  A: 

It's tricksy, but I've had good luck with code like so:

*reinterpret_cast<void**>( &(PVOID&)( DetourFunc ) ) = (PVOID) 0x00FFFF00; 

The concept, as I understand it, is you're referencing a reference, reinterpreting the reference, then dereferencing it. Bit confusing, but I can verify it works. You can also put an address on the right side (&func) and it'll work. Calling DetourFunc, using the form:

(DetourFunc)(param, param)

will call the original address or function.

Edit: This works, but it seems like a pretty heavy abuse of the language. It does work, though, and has been recommended in a few other questions here.

peachykeen
_that is so ugly!_ +1 for making it work though.
Cam
It's absolutely horrible, I know. :p Somehow it does work, and is great if you need to call a function in existing code without libraries (pass the virtual address in).
peachykeen
reinterpret_cast<void**>, I believe the COM methods use this as well, I did give this a try but not like you have here, I may try this again.
Dixon Steel
I am not sure if I understand how DetourFunc fits in? How do I set that up? (DetourFunc)(param, param), this is not a total func pointer is it?
Dixon Steel
DetourFunc should usually be a function pointer. I've used this with both regular function pointers and member function pointers (if you need to call __thiscall functions).
peachykeen
I want to try this but I have no idea how it works.
Dixon Steel
This won't even build for me.
Dixon Steel
It works fine for me, I actually copy-pasted that out of some of my code. What errors are you getting? How is your detour function defined? I use void (*func)(params) and just pass in the name ("func", in that case).
peachykeen
+1  A: 

As Jonathan Leffler and David Thornley mentioned, you aren't guaranteed that a function pointer can be converted to void* and back. A portable workaround would be to package the function pointer into a struct and to pass a pointer to that.

(Be aware that void** itself might have its own issues. You can avoid this too.)

For example:

typedef int (*SomeFuncType)(int);

struct FuncWrapper
{
    SomeFuncType func;
    void* output;
};

...
FuncWrapper funcWrapper;
funcWrapper.func = ...;

myInterface(id, &funcWrapper);

and then myInterface could be implemented as:

void myInterface(int id, FuncWrapper* funcWrapper)
{
    funcWrapper->func(...);
    funcWrapper->output = ...;
}
jamesdlin
How would I do that?
Dixon Steel
@Dixon Steel: See my revised answer.
jamesdlin
It is supposed to be a `void**`, that's how you return a `void*` via an out parameter. Otherwise the advice to use a struct to hold the function pointer is very good. The standard actually guarantees that this will work, unlike all the suggestions that involve `reinterpret_cast`. BTW you should be using `static_cast` and not `reinterpret_cast` in this instance.
Ben Voigt
@Ben Voigt: Oops, fixed the cast. (I always get that one mixed up for the `void*` case. ;) As for returning data through output parameter, the `struct` in this case could serve that purpose.
jamesdlin
Of course, then `myInterface` wouldn't need to take `void*` either can could take the `FuncWrapper` type directly (and no more explicit cast). I've updated my answer to do it that way.
jamesdlin
I wish I could do it this way but I must maintain the void** signature on my interface. I can not use the addy of the wrapper. I must call this interface via a third party DLL that I can not modify. This interface servers as a single entry point so that every time it's called the void** points to the function the DLL needs. That's why this is so difficult.
Dixon Steel
A: 
Dixon Steel
Maybe not, replacing CString with a BSTR gave me some good results, I have part of the DLLs running, thanks again everyone.
Dixon Steel