Using native plugin with callbacks in Unity editor (OS X) - macos

I have a native plugin that has callbacks back to the C# code. This is how the callbacks are setup:
C# Side (Unity):
public delegate void Callback(int value);
[DllImport("NativePlugin")]
public static extern void setup_callback(Callback callback);
[MonoPInvokeCallback(typeof(Callback))]
private static void OnCallback(int value)
{
Debug.Log("Callback called with value: " + value);
}
void Start()
{
setup_callback(OnCallback);
}
The Objective-C side (with C wrapper so you'd be able to call it from C#):
typedef void(*Callback)(int);
Callback _callback = NULL;
void setup_callback(Callback callback)
{
_callback = callback;
}
void on_something_happened(int value)
{
if(_callback)
_callback(value);
}
Now, most of the time the above code works ok, and I receive the correct values in Unity. But sometimes when I make changes to any one of my C# scripts, Unity crashes with this:
Exception Type: EXC_BAD_ACCESS (SIGABRT)
Exception Codes: KERN_INVALID_ADDRESS at 0x00000000ffffffa9
Exception Note: EXC_CORPSE_NOTIFY
Looking at Unity's Editor logs I found out that when I make changes to a C# script it recompiles Assembly-CSharp.dll and reloads it togheter with some other C# assemblies. So that made me think that this might cause the pointer saved in the native plugin to now point to an invalid address (because Assembly-CSharp.dll is now probably in a different address space). Note that it does not happen every time the dll gets reloaded, is it possible that it gets reloaded to the exact same address space sometimes?
Can I get an event from Unity when the editor reloads the DLL and then re-register the callbacks (assuming that this is the real problem)? Is this even the correct way to setup a callback from native code?

Related

Windows Moible 6.5 SDK GPS Sample Bugged

I cannot seem to find a good solution for this issue online. I have a device that is running Windows Embedded Handheld 6.5. I run the solution located at below
C:\Program Files (x86)\Windows Mobile 6.5.3 DTK\Samples\PocketPC\CS\GPS
I deploy the code to my device, not an emulator, and the code breaks with a null reference exception at
Invoke(updateDataHandler);
The solution ive seen recommends changing this to below
BeginInvoke(updateDataHandler);
But now the code breaks at Main with NullRefreceException.
Application.Run(new Form1());
Has anyone found a solution for this?
Did you alter the code? updateDataHandler is initialized in Form_Load:
private void Form1_Load(object sender, System.EventArgs e)
{
updateDataHandler = new EventHandler(UpdateData);
so that object will not be NULL. But there are other annoyances with the code, especially the Samples.Location class. You may instead use http://www.hjgode.de/wp/2010/06/11/enhanced-gps-sample-update/ as a starting point and the older one: http://www.hjgode.de/wp/2009/05/12/enhanced-gps-sampe/
The main issue with the sample is that it does not use a callback (delegate) to update the UI. If an event handler is fired from a background thread, the handler can not directly update the UI. Here is what I always use to update the UI from a handler:
delegate void SetTextCallback(string text);
public void addLog(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.txtLog.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(addLog);
this.Invoke(d, new object[] { text });
}
else
{
txtLog.Text += text + "\r\n";
}
}

Xamarin Android two way communication between Javascript and C#

I am developing an app using Xamarin Android which has a WebView displaying a web page. I want to implement a two way communication between Javascript from WebView to c#. I could call C# from Javascript using this link. However i couldn't find a way to send data back from C# to Javascript.
Is there a way to send data back and forth in this approach. I thought writing a callback in Javascript would work but how to fire it from C# code.
Now, My problem is how to call WebView from a javascript interface class. I have a Javascript interface class as mentioned https://developer.xamarin.com/recipes/android/controls/webview/call_csharp_from_javascript/
namespace ScannerAndroid
{
public class JSInterface: Java.Lang.Object
{
Context context;
WebView webView;
public JSInterface (Context context, WebView webView1)
{
this.context = context;
this.webView = webView1;
}
[Export]
[JavascriptInterface]
public void ShowToast()
{
Toast.MakeText (context, "Hello from C#", ToastLength.Short).Show ();
this.webView.LoadUrl ("javascript:callback('Hello from Android Native');");
}
}
}
The code throws an exception at LoadUrl line. java.lang.Throwable: A WebView method was called on thread 'Thread-891'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {42ce58a0} called on null, FYI main Looper is Looper (main, tid 1) {42ce58a0})
Now i am struggling how to refer the WebView from this Java script interface class
Yes. That is possible. If you are targeting KitKat or higher you can use:
webView.EvaluateJavascript("enable();", null);
Where in this case enable(); is a JS function.
If you are targeting lower API levels you can use LoadUrl();:
webView.LoadUrl("javascript:enable();");
EDIT:
The error you get where it complains on LoadUrl is because it for some reason happens on a non-UI thread.
Since you have already passed on the Context into your JavascriptInterface class, then you can simply wrap the contents of ShowToast in:
context.RunOnUiThread(() => {
// stuff here
});
Just change signature from Context to Activity and it should help you marshal you back on UI thread.

C++/CLI unhandled exception passing 3 or more parameters to delegate

Just stumbled upon strange behavior.
I have an unmanaged class (actually wrapper around some native lib):
//.h
class Wrapper
{
private:
void(*pCallback)(int, int /*, int*/);
public:
void SetCallback(void(*callback)(int, int /*, int*/));
void InvokeCallback();
};
//.cpp
void Wrapper::SetCallback(void(*callback)(int, int /*, int*/))
{
pCallback = callback;
}
void Wrapper::InvokeCallback()
{
pCallback(0, 0 /*, 0*/); //(1)
//(3)
}
And managed class which is winforms control and uses unmanaged wrapper described above:
public ref class MineControl : public System::Windows::Forms::Control
{
private:
Wrapper *pWrapper;
delegate void CallbackDelegate(int, int /*, int*/);
public:
MineControl()
{
/* rest of initialization here */
pWrapper = new Wrapper;
auto dlg = gcnew CallbackDelegate(this, &MineControl::Method);
auto ptr = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(dlg);
void(*callback)(int, int /*, int*/) = (void(*)(int, int /*, int*/))(ptr.ToPointer());
pWrapper->SetCallback(callback);
pWrapper->InvokeCallback();
}
void Method(int a, int b /*, int c*/)
{
//some code or even nothing at all
//(2)
}
}
This works fine.
Until I uncomment third parameter. After that I put breakpoint on (1). I can enter to MineControl::Mehod - (2). But everything fails on exiting this method. Point (3) become unreachable. I'm getting unhandled exception on exiting that method. Moreover being attached, VS still cannot handle that exception (all settings to debug unmanaged and managed code are set - this is the only case VS cannot catch exception). So Windows tries to handle it - standard App has stopped working window with two options - Debug and Close program. But I cannot debug because VS is still attached and either do not want to detach or app dies on VS detach.
I can wrap all parameters into some structure and this will work well. However can someone explain me why adding third parameter makes it impossible to get back from managed to unmanaged code?
I have no idea what is going on.
Environment: VS2013, x86 project, .net4.5
Ok, I'll post answer by myself. Solution is actually in Hans's comment.
Default calling convention is sdtcall but in my case I need cdecl calling convention.
Decorating delegate with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute solved my problem.
There is also а valuable note that keep delegate in a local variable is a bad idea.

Subscription to DTE events doesn't seem to work - Events don't get called

I've made an extension inside a package and I am calling the following code (occurs when a user presses a button in the toolbar):
DocumentEvents documentEvents = (DTE2)GetService(typeof(DTE));
_dte.Events.DebuggerEvents.OnEnterBreakMode += DebuggerEvents_OnEnterBreakMode;
_dte.Events.DebuggerEvents.OnEnterDesignMode += DebuggerEvents_OnEnterDesignMode;
_dte.Events.DebuggerEvents.OnContextChanged += DebuggerEvents_OnContextChanged;
_dte.Events.DocumentEvents.DocumentSaved += new _dispDocumentEvents_DocumentSavedEventHandler(DocumentEvents_DocumentSaved);
_dte.Events.DocumentEvents.DocumentOpened += new _dispDocumentEvents_DocumentOpenedEventHandler(DocumentEvents_DocumentOpened);
void DocumentEvents_DocumentOpened(Document Document)
{
}
void DocumentEvents_DocumentSaved(Document Document)
{
}
void DebuggerEvents_OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
}
void DebuggerEvents_OnContextChanged(Process NewProcess, Program NewProgram, Thread NewThread, StackFrame NewStackFrame)
{
}
private void DebuggerEvents_OnEnterDesignMode(dbgEventReason reason)
{
}
The first and the major problem is that the subscription to the event doesn't work. I've tried:
Opening new documents
Detaching from debug (thus supposedly triggering OnEnterDesignMode
Saving a document
None of these seem to have any effect and the callback functions were never called.
The second issue is that the subscription to the event line works USUALLY (the subscription itself, the callback doesn't work as described above) but after a while running the subscription line, e.g:
_dte.Events.DebuggerEvents.OnEnterBreakMode -= DebuggerEvents_OnEnterBreakMode;
Causes an exception:
Exception occured!
System.Runtime.InteropServices.InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.
at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis, IntPtr pThread)
at System.Runtime.InteropServices.UCOMIConnectionPoint.Unadvise(Int32 dwCookie)
at EnvDTE._dispDebuggerEvents_EventProvider.remove_OnEnterDesignMode(_dispDebuggerEvents_OnEnterDesignModeEventHandler A_1)
Any ideas will be welcome
Thanks!
Vitaly
Posting an answer that I got from MSDN forums, by Ryan Molden, in case it helps anyone:
I believe the problem here is how the
CLR handles COM endpoints (event
sinks). If I recall correctly when
you hit the
_applicationObject.Events.DebuggerEvents
part of your 'chain' the CLR will
create a NEW DebuggerEvents object for
the property access and WON'T cache
it, therefor it comes back to you, you
sign up an event handler to it (which
creates a strong ref between the
TEMPORARY object and your object due
to the delegate, but NOT from your
object to the temporary object, which
would prevent the GC). Then you don't
store that object anywhere so it is
immediately GC eligible and will
eventually be GC'ed.
I changed the code to store DebuggerEvents as a field and it all started to work fine.
Here is what #VitalyB means using code:
// list where we will place events.
// make sure that this variable is on global scope so that GC does not delete the evvents
List<object> events = new List<object>();
public void AddEvents(EnvDTE dte)
{
// create an event when a document is open
var docEvent = dte.Events.DocumentEvents;
// add event to list so that GC does not remove it
events.Add(docEvent );
docEvent.DocumentOpened += (document)=>{
Console.Write("document was opened!");
};
// you may add more events:
var commandEvent = dte.Events.CommandEvents;
events.Add(commandEvent );
commandEvent.AfterExecute+= etc...
}

Visual Studio: Who is writing to console?

OK, here's a good one (I think) - I'm working on an application with lots (far too many) dependency dlls, created by a team of developers. I'm trying to debug just one assembly, but the console output is 'polluted' by the Console.WriteLines and Debug.WriteLines left scattered around the code.
Is there anyway I can work out exactly which assembly a given line is coming from, so I can get the author to clean up their source?
UPDATE If you're also experiencing this kind of issue, note that there is another potential source of output messages which is any breakpoints with 'When hit' set to print a message. Having said which, this is a VERY cool feature, which can prevent the kind of problems I was having above.
Yes - replace Console.Out. Use Console.SetOut after creating a TextWriter which not only dumps the requested data to the original console, but also dumps a stack trace (and timestamp, and the requested data) to a file.
Here's some code, adapted from Benjol's answer:
(Note: you will want to adapt this code depending on whether you want a stack trace after each write, or after each writeline. In the code below, each char is followed by a stack trace!)
using System.Diagnostics;
using System.IO;
using System.Text;
public sealed class StackTracingWriter : TextWriter
{
private readonly TextWriter writer;
public StackTracingWriter (string path)
{
writer = new StreamWriter(path) { AutoFlush = true };
}
public override System.Text.Encoding Encoding
{
get { return Encoding.UTF8; }
}
public override void Write(string value)
{
string trace = (new StackTrace(true)).ToString();
writer.Write(value + " - " + trace);
}
public override void Write(char[] buffer, int index, int count)
{
Write(new string(buffer, index, count));
}
public override void Write(char value)
{
// Note that this will create a stack trace for each character!
Write(value.ToString());
}
public override void WriteLine()
{
// This is almost always going to be called in conjunction with
// real text, so don't bother writing a stack trace
writer.WriteLine();
}
protected override void Dispose(bool disposing)
{
writer.Dispose();
}
}
To use this for logging both Console.WriteLine and Debug.WriteLine to a file, make calls like this as early as possible in your code:
var writer = new StackTracingWriter(#"C:\Temp\ConsoleOut.txt");
Console.SetOut(writer);
Debug.Listeners.Add(new TextWriterTraceListener(writer));
Note that this currently doesn't also write to the original console. To do so, you'd need to have a second TextWriter (for the original console) in StackTracingWriter, and write to both places each time. Debug will however continue to be written to the original console.
Download Reflector and you can open up the mscorlib assembly, add your application's assemblies, then right click on the Console class and click Analyze and you can show all methods that reference the Console class.

Resources