none
.net function callback from unmanaged dll in Visual Studio 2005 c++ RRS feed

  • Question

  •  Hello,

     

    I am trying to interface a DLL including several functions. DLL is probably written in Delphi. I calling the majority of function in DLL is straightforward by using P/Invoke. But there is a function in DLL which requires a pointer to Callback function in managed part of code. I found out that this can be done via delegates.

     

    So I:

     

    // I declare a delegate

    public delegate void RSSIEventDelegate(System::Single^ data);

     

    //declare a function prototype, callback function and wrap all in a reference class

     

    ref class ClassMotoTrbo

    {

    public:

          [DllImport("trborssi32")]

          static void GetRSSIdata();

     

          [DllImport("trborssi32.dll",EntryPoint="RSSIEvent"]

          static public void RSSIEvent(RSSIEventDelegate^ pfn);

     

          static public void RSSIData(System::Single^ data)    // Callback function

          {    

                System::Console::Write("RSSI Value: ");

                System::Console::Write(datadata->ToString());

                System::Console::Write("     ");

     

                //System::Console::Write(tmp.ToString());

                System::Console::WriteLine(" --");

          }

     

    // In initialisation part of the Form I initialize function:

          fp = gcnew RSSIEventDelegate(ClassMotoTrbo::RSSIData);

          pin_ptr<RSSIEventDelegate^> pp = &fp;

          ClassMotoTrbo::RSSIEvent(fp);

     

    // And I call another function form DLL which run should run RSSIEvent under press button

          ClassMotoTrbo::GetRSSIdata();

     

    After successful compilation (without error) when I test the code the debugger after entering the callback function reports:

     

    An unhandled exception of type 'System.NullReferenceException' occurred in MotoTurboAPIForm.exe.

     

    When I use pointers instead of handles

                public delegate void RSSIEventDelegate(float* data);

                static public void RSSIData(float* data)

    The program works without debugger reports but it seems to me the pointer data does not point to correct memory location.

    I know the DLL is working because I have Delphi example which works.

     

    Help please

     

    Tomaž

     

     

     

     

    Wednesday, October 15, 2008 5:40 AM

All replies

  • The first thing I would check is that you don't have a calling convention mismatch. The CLR will assume _stdcall by default, but you can use the UnmanagedFunctionPointerAttribute to change that.

    Also, it's not clear if the code calling RSSIEvent is in the same method as your call to GetRSSIdata? Keep in mind that your pin_ptr will only keep the delegate pinned as long as it's in scope.


    Mattias, C# MVP
    Wednesday, October 15, 2008 7:37 AM
  • The argument type is surely wrong, using the "hat" on a value type like System::Single makes no sense.  If the callback just passes a simple float, just drop the hat.  If it passes an array, declare the argument of type System::IntPtr and marshal the array data yourself with Marshal.Copy().  That might be tricky to do if you don't know the array size.
    Hans Passant.
    Wednesday, October 15, 2008 11:28 AM
    Moderator
  • I have removed the hat, but the result is the same as when I use float* declaration of agruments.  The program is running but the value printed on console is constant and farm from values expected.


    The calling convention is StdCall. That I can see from delphi example.

    Do you have any other suggestion?

    Regards

    Tomaz
    Wednesday, October 15, 2008 2:21 PM
  • You posting the Delphi declaration of the callback would make rather a lot of sense right now.
    Hans Passant.
    Wednesday, October 15, 2008 2:47 PM
    Moderator
  •  

    Hi,

     

    Hire is Delphi declarations

     

    type  TRSSIdata = procedure(var signal: real) of Object;

     

    procedure RSSIEvent(OnRSSIdata: TRSSIdata); stdcall;

    procedure GetRSSIdata(); stdcall;

    procedure DisconnectRadio(); stdcall; // this works fine in .net

    function  InitRadioSocket(Radio_TCPIP: pchar): boolean; stdcall; // this works fine in .net

     

    const dllName = 'trborssi32.dll';

    function  InitRadioSocket; external dllName;

    procedure RSSIEvent; external dllName;

    procedure GetRSSIdata; external dllName;

    procedure DisconnectRadio; external dllName;

     

    // the initialization of the form

    procedure TMainForm.FormCreate(Sender: TObject);

    begin

         RSSIEvent(RssiData);

    end;

     

    // periodic call of procedure

    procedure TMainForm.TimerGetTimer(Sender: TObject);

    begin

        GetRSSIdata();

    end;

     

    // local function which display data on screen

    procedure TMainForm.RssiData(var data: real);

    var i: integer;

    begin

       i:=141+trunc(data);

       ProgressBar.Position:=i;

       LabelData.Caption:='RSSI: ' + VarToStr(data) +' dB';

    end;

     

    Regards

     

    Tomaz

    Wednesday, October 15, 2008 5:50 PM
  • If I remember my Turbo Pascal correctly, the argument is passed by reference.  The equivalent delegate declaration is:

      public delegate void RSSIEventDelegate(System::Single% data)

    When I tried it out in C++/CLI, I had trouble making it work for float and had to switch to double.  Not sure why, it might be a show-stopper.

    Hans Passant.
    Wednesday, October 15, 2008 6:25 PM
    Moderator
  • Hi,

    I have try new declaration of delegate by passing argument by reference, the results is the same as before.

    Regards

    Tomaz
    Thursday, October 16, 2008 5:20 AM
  • Well, that's a good sign.  It should have crashed and burned badly with an access violation if Delphi didn't pass it by reference.
    Hans Passant.
    Thursday, October 16, 2008 9:45 AM
    Moderator
  •  Do you have any other suggestion?

    Tomaž
    Thursday, October 16, 2008 10:29 AM