locked
cannot convert from 'void (__clrcall*)...' to 'void (__cdecl*)...'

    Question

  • Hi!

     

    I've a windows forms application containing a OpenGL box. I'd like to react on keyboard- and mouse-events.

     

    The following line causes a compiler error:

    Code Snippet
    glutKeyboardFunc(OpenGLForm::VectorPlot3DOGL::glutKeyboard);

     

     

    Where

    static void glutKeyboard(unsigned char key,int x,int y);

     is the function that should handle keyboard events.

     

    My compiler says:

    Code Snippet

    error C2664: 'glutKeyboardFunc' : cannot convert parameter 1 from 'void (__clrcall *)(unsigned char,int,int)' to 'void (__cdecl *)(unsigned char,int,int)'

    None of the functions with this name in scope match the target type

     

     

    I also tried different variations of calling glutKeyboardFunc ( &-operator, adding ' ( ) '...) but had no success. Other errors occured, which (at least  for me) seemed to be farer away from solving the problem than the line given above.

     

    Does anyone have an idea what the problem might be?

     

    volkers

    Friday, September 21, 2007 9:45 PM

Answers

  • You're getting closer, now you need to cast the return value of GetFunctionPointerForDelegate (a void*) to the required type:

      typedef void (__cdecl * GLUTKEYPROC)(unsigned char key, int x, int y);
    ...
      glutKeyboardFunc((GLUTKEYPROC)System::Runtime:: InteropServices::Marshal::GetFunctionPointerForDelegate(dglutkb).ToPointer());

    Your OpenGL header files probably already contain a typedef for the function pointer.  If it does, you should use that one instead.

    Be sure to make "dglutkd" a member of a class who's instance lives a long as your program.  Your main form would qualify.  Failure to do so would crash and burn your app after the garbage collector deletes the delegate instance.  It cannot track references to managed objects held by unmanaged code.

    A completely different approach is to make your keyboard handler a static __cdecl function outside of a class definition.  No need to tinker with delegates.  It would work just as well as your current approach since you made the managed handler a static method.  You'll have a hard time getting a reference to your form within that static method.  If you actually want to make the managed handler an instance method, you'd initialize the delegate like this:

      DglutKeyboard^ dglutkb;  // Not static

      void glutKeyboard(unsigned char key, int x, int y) {
        // Managed keyboard handler, not static, you can use "this"
      }

    ...
        dglutkb = gcnew DglutKeyboard(this, &Form1::glutKeyboard);

    Saturday, September 22, 2007 1:36 PM
    Moderator

All replies

  • What is the error if you do the following:

    Code Snippet

    glutKeyboardFunc(&OpenGLForm::VectorPlot3DOGL::glutKeyboard);

     

    [Which is what the C++ Standard requires]

     

    Also what are OpenGLForm and VectorPlot3DOGL: are they ref classes?

    Friday, September 21, 2007 10:01 PM
  • Adding an '&' :

    Code Snippet

    error C3374: can't take address of 'OpenGLForm::VectorPlot3DOGL::glutKeyboard' unless creating delegate instance

    OpenGL.cpp

     

     

    public ref class VectorPlot3DOGL: OpenGL

    is derived from

    public ref class OpenGL abstract : public System::Windows::Forms::NativeWindow

     

    An instance of VectorPlot3DOGL is created when the user offers a proper input file (= data for a 3D vector plot).

     

    [Edit] Both classes are inside "namespace OpenGLForm {...}" [/Edit]

     

    Do I have to create a delegate instance? How does that work?

     

    Thank you in advance!

     

    volkers

     

    Friday, September 21, 2007 10:21 PM
  • Yes: you can't directly take the address of a member of a ref class and pass it to a native function. You are going to have to create a delegate. This article might help.

    Friday, September 21, 2007 10:24 PM
  • Thank you for your quick reply!  Here's what I've done now:

     

    I added

    Code Snippet
    delegate
    void DglutKeyboard(unsigned char key,int x,int y);

     

     

    to the header file of the parent class OpenGL and

     

    Code Snippet

    static DglutKeyboard^ dglutkb;

     

     

    as a private variable to the header file of VectorPlot3DOGL.

     

    In VectorPlot3DOGL I replaced the call to glutKeyboardFunc(&[...]glutKeyboard) by the following lines:

     

    Code Snippet

     

    dglutkb = gcnew DglutKeyboard(glutKeyboard);

    glutKeyboardFunc((DglutKeyboard^)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(dglutkb).ToPointer());

     

     

    But the cast to (DglutKeyboard^) fails:

    Code Snippet

     

    error C2440: 'type cast' : cannot convert from 'void *' to 'DglutKeyboard ^'

    No user-defined-conversion operator available, or

    Cannot convert an unmanaged type to a managed type

     

     

    Ommiting this cast I get:

    Code Snippet

    error C2664: 'glutKeyboardFunc' : cannot convert parameter 1 from 'void *' to 'void (__cdecl *)(unsigned char,int,int)'

    Conversion from 'void*' to pointer to non-'void' requires an explicit cast

     

     

     

    What am I doing wrong?  I always thought I'm not such a programming noob, but this managed C++ stuff really scares me a little! 

     

     

     

     

    Saturday, September 22, 2007 12:01 AM
  • You're getting closer, now you need to cast the return value of GetFunctionPointerForDelegate (a void*) to the required type:

      typedef void (__cdecl * GLUTKEYPROC)(unsigned char key, int x, int y);
    ...
      glutKeyboardFunc((GLUTKEYPROC)System::Runtime:: InteropServices::Marshal::GetFunctionPointerForDelegate(dglutkb).ToPointer());

    Your OpenGL header files probably already contain a typedef for the function pointer.  If it does, you should use that one instead.

    Be sure to make "dglutkd" a member of a class who's instance lives a long as your program.  Your main form would qualify.  Failure to do so would crash and burn your app after the garbage collector deletes the delegate instance.  It cannot track references to managed objects held by unmanaged code.

    A completely different approach is to make your keyboard handler a static __cdecl function outside of a class definition.  No need to tinker with delegates.  It would work just as well as your current approach since you made the managed handler a static method.  You'll have a hard time getting a reference to your form within that static method.  If you actually want to make the managed handler an instance method, you'd initialize the delegate like this:

      DglutKeyboard^ dglutkb;  // Not static

      void glutKeyboard(unsigned char key, int x, int y) {
        // Managed keyboard handler, not static, you can use "this"
      }

    ...
        dglutkb = gcnew DglutKeyboard(this, &Form1::glutKeyboard);

    Saturday, September 22, 2007 1:36 PM
    Moderator
  • First of all: Thanks for your help, guys!

     

    Alright... good news first: I don't get any compiler/linker errors anymore!

    (but stress the words "compiler" and "linker") Smile

     

    I tried two approaches:

     

    I removed all the previous code from classes OpenGL and VectorPlot3DOGL : OpenGL.

    Then I modified Form1.h like that:

     

    Code Snippet

    static void __cdecl glutKeyboard(unsigned char key,int x,int y)

    {

    if (key == 27) Application::Exit();  // not doing much at the moment, I know

    }

     

    public ref class Form1 : public System::Windows::Forms::Form

    {

    public:

    Form1(void)

    {

    InitializeComponent();

    glutKeyboardFunc(&glutKeyboard); // fails

    [...]

     

     

     

    Running the program causes my Micro$oft Vi$ta trying to find a solution concerning a problem with my application. Great description, I have to say.  Using the debugger, I see it's a

    "An unhandled exception of type 'System.AccessViolationException' occurred in myapp.exe"

    occuring when glutKeyboardFunc is called.

     

    The same thing happens when I call "glutKeyboardFunc(glutKeyboard);" instead.

     

    I even tried it using a delegate object:

     

    Fom1.h:

     

    Code Snippet

    delegate void DglutKeyboard(unsigned char key,int x,int y);

    typedef void (__cdecl * GLUTKEYPROC)(unsigned char key, int x, int y);

    static void __cdecl glutKeyboard(unsigned char key,int x,int y)

    {

    if (key == 27) Application::Exit(); // not doing much at the moment, I know

    }

     

    public ref class Form1 : public System::Windows::Forms::Form

    {

    public:

    Form1(void)

    {

    InitializeComponent();

    DglutKeyboard^ dglutkb = gcnew DglutKeyboard(glutKeyboard);

    glutKeyboardFunc((GLUTKEYPROC)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(dglutkb).ToPointer()); // fails, too

     

    [...]

     

     

    I leads to the same exception.

     

    Maybe I should notice that prior to that error, after it compiled for the first time successfully, I got an other error when the program came up: A failure box opened and told me, that my glut32.dll is borked or maybe not intended for use on a windows machine. I got the latest version from the web an that error got away.

     

    It is a little bit frustrating ... I'm working now for more than 2 days on passing a damn function pointer to another function! Does it have to be so difficult?

     

     

    Saturday, September 22, 2007 9:25 PM
  • Yeeha! I think the changes nobugz suggested really work in principle. I'm simply too dumb initializing my glut correctly!! That's great! 

     

    I noticed that other calls to different glutXXXX() -functions lead to the same exception as well. So I'll work on that issue and assume my problem concerning passing a function pointer is solved. Smile

     

    Thanks a lot Jonathan Caves - MSFT  and nobugz !!! You really helped me out!

     

    Greetz

    volkers

    Sunday, September 23, 2007 5:21 PM