I am trying to create a managed c++ wrapper and have run into a problem is when SetNewNetworkCallBack is run. It doesn't like that NetworkID isn't an UInt16. I get the error:
CCommManager::SetNewNetworkCallBack' : cannot convert parameter 1 from 'void (unsigned short,void *)' to 'void (__cdecl *)(UInt16,void *)'
Any help would be appreicated. Offending code is below.
James
Managed Code:
CCommApp::CCommApp(char chan, long baud, String * ServerAddress, int ListenPort, bool Logging) { m_pComm->SetNewNetworkCallBack(NewNetwork, this); //Problem line
}
void CCommApp::NewNetwork(unsigned short NetworkID, void *Parameter)
<james.cro...@wpafb.af.mil> wrote: >I am trying to create a managed c++ wrapper and have run into a problem is >when SetNewNetworkCallBack is run. It doesn't like that NetworkID isn't an >UInt16. I get the error:
>CCommManager::SetNewNetworkCallBack' : cannot convert parameter 1 from 'void >(unsigned short,void *)' to 'void (__cdecl *)(UInt16,void *)'
>Any help would be appreicated. Offending code is below.
>James
>Managed Code:
>CCommApp::CCommApp(char chan, long baud, String * ServerAddress, int >ListenPort, bool Logging) >{ > m_pComm->SetNewNetworkCallBack(NewNetwork, this); //Problem >line >}
>void CCommApp::NewNetwork(unsigned short NetworkID, void *Parameter)
This has got nothing to do with managed vs unmanaged code or UINT16. Apparently NewNetwork is a non-static member function, and VC7.1 is not only allowing the illegal syntax for referring to it in your SetNewNetworkCallBack call, it's emitting a misleading error message which implies it's a static member. IIRC, VC8 fixes the syntax problem, so the bare "NewNetwork" reference will be disallowed; instead, you'll have to use &CCommApp::NewNetwork to form a pointer to member. In any case, the solution is to make NewNetwork static.
P.S. It would be useful in the future to include the complete text of the error message, including error number, and to abstract the problem into a simple console program. For example (removing all the managed stuff doesn't affect the error message):
#using <mscorlib.dll> using namespace System;
#pragma unmanaged
void f(void (*)(unsigned short)) {
}
#pragma managed
struct X { void g(unsigned short) { }
void h() { f(g); }
};
int main() {
}
C>ccr k.cpp Microsoft (R) C/C++ Optimizing Compiler Version 13.10.3077 for .NET Framework Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.
k.cpp k.cpp(20) : error C2664: 'f' : cannot convert parameter 1 from 'void (unsigned short)' to 'void (__cdecl *)(unsigned short)' None of the functions with this name in scope match the target type
James Crouch wrote: > I am trying to create a managed c++ wrapper and have run into a problem is > when SetNewNetworkCallBack is run. It doesn't like that NetworkID isn't an > UInt16. I get the error:
> CCommManager::SetNewNetworkCallBack' : cannot convert parameter 1 from 'void > (unsigned short,void *)' to 'void (__cdecl *)(UInt16,void *)'
> Any help would be appreicated. Offending code is below.
> James
> Managed Code:
> CCommApp::CCommApp(char chan, long baud, String * ServerAddress, int > ListenPort, bool Logging) > { > m_pComm->SetNewNetworkCallBack(NewNetwork, this); //Problem > line > }
> void CCommApp::NewNetwork(unsigned short NetworkID, void *Parameter)
Is CCommApp::NewNetwork static? The NewNetworkCallBack prototype requires that the function is non-member or at least static. Just move the NewNetwork function outside of the CComApp class, it doesn't belong there.
Also check that UInt16 is exactly the same type as unsigned short (better to rename Uint16 to unsigned short), and the calling conventions must match too (both __stdcall or __cdecl, better to specify it explicitly).
If I see it correctly, you're passing a managed method as an unmanaged pointer. That's not going to work. You have to write a global unmanaged function (in MC++ or C++/CLI) that calls back to the managed code, and pass this intermediate function to the unmanaged API. There's no way you can pass a managed callback to an unmanaged library without this intermediate layer.
Also, you can't pass a managed void* to the unmanaged part either, at least not without pinning it.
> This has got nothing to do with managed vs unmanaged code or UINT16. > Apparently NewNetwork is a non-static member function, and VC7.1 is not > only allowing the illegal syntax for referring to it in your > SetNewNetworkCallBack call, it's emitting a misleading error message which > implies it's a static member. IIRC, VC8 fixes the syntax problem, so the > bare "NewNetwork" reference will be disallowed; instead, you'll have to > use > &CCommApp::NewNetwork to form a pointer to member. In any case, the > solution is to make NewNetwork static.
> P.S. It would be useful in the future to include the complete text of the > error message, including error number, and to abstract the problem into a > simple console program. For example (removing all the managed stuff > doesn't > affect the error message):
> #using <mscorlib.dll> > using namespace System;
> #pragma unmanaged
> void f(void (*)(unsigned short)) > { > }
> #pragma managed
> struct X > { > void g(unsigned short) > { > }
> void h() > { > f(g); > } > };
> int main() > { > }
> C>ccr k.cpp > Microsoft (R) C/C++ Optimizing Compiler Version 13.10.3077 for .NET > Framework > Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.
> k.cpp > k.cpp(20) : error C2664: 'f' : cannot convert parameter 1 from 'void > (unsigned short)' to 'void (__cdecl *)(unsigned short)' > None of the functions with this name in scope match the target type
> -- > Doug Harrison > VC++ MVP
Thanks for the info and advice. But how do I go about creating that pointer to member? Thanks.
I've pulled the NewNetwork method out and made it global which solved my inital problem. Now however, I an error while trying to pass "this" to NewNetwork. The "void *Parameter" is an echo of SetNewNetworkCallBack's "this". Do I need to do some pinning and if so how?
Thanks.
James
"Tamas Demjen" <tdem...@yahoo.com> wrote in message
> If I see it correctly, you're passing a managed method as an unmanaged > pointer. That's not going to work. You have to write a global unmanaged > function (in MC++ or C++/CLI) that calls back to the managed code, and > pass this intermediate function to the unmanaged API. There's no way you > can pass a managed callback to an unmanaged library without this > intermediate layer.
> Also, you can't pass a managed void* to the unmanaged part either, at > least not without pinning it.
James Crouch wrote: > I've pulled the NewNetwork method out and made it global which solved my > inital problem. Now however, I an error while trying to pass "this" to > NewNetwork. The "void *Parameter" is an echo of SetNewNetworkCallBack's > "this". Do I need to do some pinning and if so how?
> Thanks.
> James
You probably figured out that I replied without noticing James' post.
Well, I believe you're taking chances if you don't pin the "this". What happens is that you pass the managed handle "this" to an unmanaged code, which will in turn call back to the managed code passing back the "this". The problem is that the .NET framework may move data around in the memory in the meantime. I don't know how safe it is to pass a GC handle to an unmanaged code, which passes the same handle back to managed code. When you convert the GC handle into unmanaged void*, you essentially store its internal representation (IntPtr). If the GC decides to compact the heap, small objects may be moved around in the memory, unless they are pinned. Now what happens if your "this" handle is moved around while your unmanaged code is being executed? My rule of thumb is to never get an IntPtr for a managed object without first pinning it.
I was curious how to do this, and worked out two very simple examples. This is how I would implement a C-callback in managed C++ code. I couldn't find anything that allowed a managed function to be passed directly to an unmanaged call (first I couldn't convert between void* and the managed type, second I couldn't convert between __clrcall and __cdecl functions). So I had to introduced the intermediate class "Bridge". gcroot<> is a wrapper for GCHandle, which allows us to store a managed handle in a native class. It also ensures that the managed class is pinned, so the garbage collector doesn't move around the "c" object while the native code is running. After the native "Process" is done, the bridge goes out of scope, so the pin gets released.
////////////////////////////////////////////////////////////////// // For MC++: #include "stdafx.h" #include <vcclr.h>
IMO it's more flexible when using (typesafe) delegates...
#pragma unmanaged class Unmanaged { public: void Process( void (__stdcall *ptr) (int)) { int i = 10; if(ptr != 0) ptr(i); }
};
// managed code from here #pragma managed public __gc class C { public: static C* m_pClass = 0; __delegate void CbckProc(int v); private: // private inner non GC class used as thunk to callback into managed code __nogc class _C { // private: public: static void __stdcall CallbackProc(int v) { m_pClass->m_cbProc->Invoke(v); // Invoke delegate target } };
>I was curious how to do this, and worked out two very simple examples. This >is how I would implement a C-callback in managed C++ code. I couldn't find >anything that allowed a managed function to be passed directly to an >unmanaged call (first I couldn't convert between void* and the managed >type, second I couldn't convert between __clrcall and __cdecl functions). >So I had to introduced the intermediate class "Bridge". gcroot<> is a >wrapper for GCHandle, which allows us to store a managed handle in a native >class. It also ensures that the managed class is pinned, so the garbage >collector doesn't move around the "c" object while the native code is >running. After the native "Process" is done, the bridge goes out of scope, >so the pin gets released.
> Willy Denoyette [MVP] wrote: >> IMO it's more flexible when using (typesafe) delegates...
> Good idea, thanks Willy.
>> // private inner non GC class used as thunk to callback into managed >> code >> __nogc class _C
> VC++ 2005 doesn't allow me to define an unmanaged class in a managed one:
> ref class Managed > { > private: > class Unmanaged > { > }; > };
> error C2814: 'Managed::Unmanaged' : a native type cannot be nested within > a managed type 'Managed' > Tom
Tom,
I know, and it's unfortunate C++/CLI does not support nested native types (or mixed types), so you'll have to use the same "hack" as you did to make it work but now using delegates. I guess there might be a cleaner way to achieve the same.
#pragma unmanaged class Unmanaged { public: void Process(void (__stdcall *ptr) (void*, int), void* param) { int i = 10; if(ptr != 0) ptr( param, i); }
};
// managed code from here #pragma managed
public ref class C { public: delegate void CbckProc(int);
class Thunk { // private: public: Thunk(C^ in):m_pClass(in){} static void __stdcall CallbackProc(void* param, int v) { // here is the "ugly" cast back again static_cast<Thunk*>(param)->m_pClass->m_cbProc->Invoke(v); } gcroot<C^> m_pClass; };
> "Tamas Demjen" <tdem...@yahoo.com> wrote in message > news:eT2tRhsvFHA.916@TK2MSFTNGP10.phx.gbl... >> Willy Denoyette [MVP] wrote: >>> IMO it's more flexible when using (typesafe) delegates...
>> Good idea, thanks Willy.
>>> // private inner non GC class used as thunk to callback into managed >>> code >>> __nogc class _C
>> VC++ 2005 doesn't allow me to define an unmanaged class in a managed one:
>> ref class Managed >> { >> private: >> class Unmanaged >> { >> }; >> };
>> error C2814: 'Managed::Unmanaged' : a native type cannot be nested within >> a managed type 'Managed' >> Tom
> Tom,
> I know, and it's unfortunate C++/CLI does not support nested native types > (or mixed types), so you'll have to use the same "hack" as you did to make > it work but now using delegates. I guess there might be a cleaner way to > achieve the same.