• Visitors can check out the Forum FAQ by clicking this link. You have to register before you can post: click the REGISTER link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below. View our Forum Privacy Policy.
  • Want to receive the latest contracting news and advice straight to your inbox? Sign up to the ContractorUK newsletter here. Every sign up will also be entered into a draw to WIN £100 Amazon vouchers!

C# to C++ callback problem

Collapse
X
  •  
  • Filter
  • Time
  • Show
Clear All
new posts

    C# to C++ callback problem

    I am trying to write a dll in C++ that a C# app can register a callback with. There are plenty of examples on the web, but none that I can get to work. They compile and run, and don't give any errors, but just don't work.

    The C++ code

    Code:
    extern "C" _declspec(dllexport) void registerCallBack(void (_stdcall *func)()){
      //Just call the callback once for now
      func();
      return;
    }
    The C# code

    Code:
    public delegate void CallBackDelegate();
    
    [DllImport("MyDll"]
    public static extern void registerCallBack(CallBackDelegate del);
    
    static void myCallBack()
    {
      Console.WriteLine("Callback fired!");
      return;
    }
    
    static void Main(string[] args)
    {
    
      CallBackDelegate del = new CallBackDelegate(myCallBack);
      registerCallBack(del);
      Console.WriteLine("Callback registered");
    
    }
    The code in the C# Main function hits the registerCallBack line, and if I F10 it, the IDE just unloads. No error, nothing.

    This code looks good to me from the tutorials I have been reading online, can anyone spot the glaring error?
    Knock first as I might be balancing my chakras.

    #2
    Shouldn't the first line be

    [DllImport("MyDll")] note the additional ) .

    Either way is the dll you are talking to registered and accessible to the .net code?
    merely at clientco for the entertainment

    Comment


      #3
      Originally posted by eek View Post
      Shouldn't the first line be

      [DllImport("MyDll")] note the additional ) .

      Either way is the dll you are talking to registered and accessible to the .net code?
      It isn't registered as the dll is not a com object, rather a good old fashioned dll.
      Knock first as I might be balancing my chakras.

      Comment


        #4
        Won't your console app just load run and then exit?

        What's keeping it running?

        Comment


          #5
          Originally posted by DimPrawn View Post
          Won't your console app just load run and then exit?

          What's keeping it running?
          The api call. In point of fact I actually have a loop that runs in the dll that keeps firing the callback.

          I have had some more success in that if I pass no parameters at all, and declare the function in the dll as _cdecl and then may the DllImport use CallingConvention.CDecl it works.

          So then I added the parameters back in and I get an access violation.
          Knock first as I might be balancing my chakras.

          Comment


            #6
            Originally posted by suityou01 View Post
            In point of fact I actually have a loop that runs in the dll that keeps firing the callback.
            What happens without the loop? i.e. if your code just calls registerCallback(del) does the c++ code receive a valid function pointer at that point?

            I suspect that if you are keeping a copy of the pointer then you will hit problems because it points at a system generated "thunk" into managed code that is probably getting tidied up.

            Interesting reading here: http://www.mono-project.com/Interop_...tive_Libraries

            The GC that .NET uses is not conservative. It knows all types involved, and can distinguish between an integer that looks like a pointer and an actual pointer value. It knows all stack-allocated variables, and what the scope of those variables is. Finally, the GC does not see into unmanaged code.

            The result of this is that it's possible the the GC to collect a class instance while a method of the instance is still executing .

            How is this possible? If the method no longer references class data (instance members), and no other code refers to it, the GC may collect the class. After all, if no instance members are used and no one is using the instance, what's it matter if the instance is collected?
            So perhaps try and find a way to trick you last Console.writeLine(); into keeping a reference to an instance variable e.g. by storing the message as a member.
            Last edited by doodab; 26 May 2011, 14:29.
            While you're waiting, read the free novel we sent you. It's a Spanish story about a guy named 'Manual.'

            Comment


              #7
              Originally posted by doodab View Post
              What happens without the loop? i.e. if your code just calls registerCallback(del) does the c++ code receive a valid function pointer at that point?

              I suspect that if you are keeping a copy of the pointer then you will hit problems because it points at a system generated "thunk" into managed code that is probably getting tidied up.
              The loop just calls the function pointer. That's it.

              The function pointer is valid, and the api blocks the thread so it's not going to get cleaned up (I hope).

              I have managed to pass primitive arguments to the callback, but not my struct, which gives the latest error. Presumably the managed code cannot marshal the structure. Whichever, having a callback with a very lot of arguments is a small price to pay if it's robust.
              Knock first as I might be balancing my chakras.

              Comment


                #8
                Does this help?

                Gotchas with Reverse Pinvoke (unmanaged to managed code callbacks) - David Notario's WebLog - Site Home - MSDN Blogs

                Comment


                  #9
                  Originally posted by suityou01 View Post
                  The loop just calls the function pointer. That's it.

                  The function pointer is valid, and the api blocks the thread so it's not going to get cleaned up (I hope).

                  I have managed to pass primitive arguments to the callback, but not my struct, which gives the latest error. Presumably the managed code cannot marshal the structure. Whichever, having a callback with a very lot of arguments is a small price to pay if it's robust.
                  What do you mean the API blocks the thread exactly? The GC runs in a different thread.

                  What struct are you passing into the callback? Why don't you post the actual code with the problems?
                  While you're waiting, read the free novel we sent you. It's a Spanish story about a guy named 'Manual.'

                  Comment


                    #10
                    Originally posted by doodab View Post
                    What do you mean the API blocks the thread exactly? The GC runs in a different thread.

                    What struct are you passing into the callback? Why don't you post the actual code with the problems?
                    I posted stripped down code for brevity. The original problem was the callback not working. Having pedalled backwards to no parameters, and then primitive parameters I have something that seems to work.

                    C++

                    Code:
                        typedef enum FWP_DIRECTION_ {
                    	FWP_DIRECTION_OUTBOUND,
                    	FWP_DIRECTION_INBOUND,
                    	FWP_DIRECTION_MAX
                        } FWP_DIRECTION;
                    
                            LIST_ENTRY listEntry;
                    		int srcport;
                            int destport;
                            char sourceIp[16];
                            char destIp[16];
                    		int protocol;
                    		long packetRef;
                    		FWP_DIRECTION  direction; //FWP_DIRECTION Defined in Inspect.h
                    		int type;
                    		UINT64 processId;
                    		int allow;
                    } FIREWALL_MESSAGE;
                    
                    
                    
                    extern "C" _declspec(dllexport) void Cpkdll::listenForALEConnections(void (_stdcall *func)(int destPort, int srcPort))
                    {
                    	m_bListenForALEConnections=true;
                    	FIREWALL_MESSAGE fwm;
                    	while (m_bListenForALEConnections==true)
                    	{
                    		strcpy(fwm.destIp,"212.58.254.251");
                    		fwm.destport = 80;
                    		strcpy(fwm.sourceIp,"192.168.1.5");
                    		fwm.packetRef = 111;
                    		fwm.processId = 0;
                    		fwm.protocol = 53;
                    		fwm.srcport = 2453;
                    		fwm.type = 1;
                    		func(fwm.destport,fwm.srcport);
                    		Sleep(2000);
                    	}
                    	return;
                    }
                    You can see here that I was trying to pass FIREWALL_MESSAGE back to CLR, to no avail, and then swapped to two ints for simplicity's sake.

                    C#

                    Code:
                    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
                            public struct LIST_ENTRY
                            {
                                /// _LIST_ENTRY*
                                public System.IntPtr Flink;
                    
                                /// _LIST_ENTRY*
                                public System.IntPtr Blink;
                            }
                    
                            public struct FIREWALL_MESSAGE
                            {
                                public LIST_ENTRY listEntry;
                                public Int32 srcport;
                                public Int32 destport;
                                public char[] sourceIp;
                                public char[] destIp;
                                public Int32 protocol;
                                public Int32 packetRef;
                                public EFWPDIRECTION direction; //FWP_DIRECTION Defined in Inspect.h
                                public Int32 type;
                            }
                    
                    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
                            public delegate void CallBackMethodDelegate(int destPort, int srcPort);
                    
                            [DllImport("C:\\Projects\\portknox\\User Mode Dll\\pk.dll\\Debug\\pk.dll.dll", EntryPoint = "?listenForALEConnections@Cpkdll@@QAEXP6GXHH@Z@Z", CallingConvention = CallingConvention.StdCall)]
                            public static extern void listenForALEConnections(CallBackMethodDelegate del);
                    
                    
                            static void aleCallBack(int destPort, int srcPort)
                            {
                                Console.WriteLine(destPort.ToString() + "," + srcPort.ToString());
                                return;
                            }
                    
                            static void Main(string[] args)
                            {
                                CallBackMethodDelegate del = new CallBackMethodDelegate(aleCallBack);
                                listenForALEConnections(del);
                                Console.WriteLine("Listening for ALE Connections");
                            }
                    So there you go, the whole sordid affair.

                    @DimPrawn : Yes very good blog post, given me lots to think about C++ side.
                    Knock first as I might be balancing my chakras.

                    Comment

                    Working...
                    X