C++/CLI unhandled exception passing 3 or more parameters to delegate - visual-studio-2013

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.

Related

A crash on V8' Context::New

I implement a wrapper around the Google V8 engine. I wrote a class:
class Es
{
public:
Es();
~Es();
int Init(const char* exec_path);
int CreateContext(uint& id);
int RemoveContext(const uint id);
protected:
Global<Context> global_context;
std::map<uint, Persistent<Context>*> contexts;
Isolate* isolate = nullptr;
private:
uint next_id = 1;
};
I want to create Contexts, hold them in the contexts var, and remove them oneday. So, I init the V8 engine:
int Es::Init(const char* exec_path)
{
v8::V8::InitializeICUDefaultLocation(exec_path);
v8::V8::InitializeExternalStartupData(exec_path);
std::unique_ptr<Platform> platform = platform::NewDefaultPlatform();
V8::InitializePlatform(platform.get());
V8::Initialize();
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = ArrayBuffer::Allocator::NewDefaultAllocator();
isolate = Isolate::New(create_params);
if (!isolate)
return InitError;
return Success;
}
And after that I want to create a context, using int Es::CreateContext(uint& id). Its called after Init.
int EasyProspect::CreateContext(uint& id)
{
if (!isolate)
return NotInitializedError;
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Context> local_context = Context::New(isolate);
Persistent<Context> context(isolate, local_context);
contexts.emplace(id, &context);
return Success;
}
But I can't do that, the code crashes on Context::New(isolate). Why? isolate is not null, I enter the local scope...
Your best bet is to compile in Debug mode and run in a debugger. Then it should be easy to tell what's causing the crash.
(At the very least, you should post a complete reproducible example, including specifying the V8 version you're working with, how that's built/configured, and how you're compiling your code.)
If I had to guess: the Platform and the ArrayBuffer::Allocator need to stay alive for as long as you want to use the V8 instance, but in your code they are both destroyed at the end of Es::Init. Since Es is a wrapper class, you can easily add fields there to keep them around.

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

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?

Java debugger can't call some default method implementations

I'm coding in IntelliJ IDEA. When debugging my application, I can't use some default method implementations in Watches.
Here is a condensed example:
public class Friendship {
interface Friend {
default void sayHiTo(Friend friend) {
System.out.println("Hi, " + friend.hashCode());
}
default int amountOfHands() {
return 2;
}
}
public static class BasicFriend implements Friend {
int numberOfFaces() {
return 1;
}
}
public static void main(String[] args) {
System.out.println("Put a breakpoint here");
}
}
In main() method I put a breakpoint and set up three watches:
// Default interface method with dependency
new BasicFriend().sayHiTo(new BasicFriend())
// Default interface method without dependency
new BasicFriend().amountOfHands()
// Class method
new BasicFriend().numberOfFaces()
The first watch throws NoSuchMethodException complaining that method Friendship$BasicFriend.sayHiTo() doesn't exist.
The second watch runs successfully, but strangely it reports a boxed object
{java.lang.Integer#537} "2" instead of just a primitive 2.
The third watch reports a primitive 1, just as expected.
Why is the first watch not working? Is this a bug? Is this actually IDE related? Is it because of some conceptual flaw of default methods? Should it be working as I want it to in the first place? Is the strange result of the second watch somehow related to the issue in the first watch?
Prior to JDK 8u40, default and static interface methods were not supported by JDI (Java Debugger Interface), JDWP (Java Debugger Wire Protocol) and JDB (the standard Java debugger). This is bug JDK-8042123, which is recorded as fixed in 8u40 and a corresponding blurb appears in the 8u40 release notes.
Update to 8u40 or later to fix this issue, at least on the JDK side.
From the bug description, it looks like debugger-side changes are also required, to avoid casting com.sun.jdi.InterfaceType objects to com.sun.jdi.ClassType, but instead call InterfaceType.invokeMethod() directly.
In the specific case of IntelliJ, Suseika confirmed in a comment that 14.1.2 has mostly fixed the issue (except the unexpected boxing), though Mike Kobit still experiences this problem on that version with a ClassCastException suggestive of the incorrect cast above.

No call to CWinThread:ExitInstance

In my dialog based application, in CWinApp::InitInstance I create class that has framed window v1:
creating modHolder
CWinThread* pThread= AfxBeginThread(RUNTIME_CLASS(modHolder));
declaring modHolder
class modHolder : public CWinThread
{
DECLARE_DYNCREATE(modHolder)
protected:
modHolder(); // protected constructor used by dynamic creation
virtual ~modHolder();
public:
CMainWindow * v1;
virtual BOOL InitInstance();
virtual int ExitInstance();
protected:
DECLARE_MESSAGE_MAP()
};
During application close I expect to get call to modHolder::ExitInstance()
Unfortunately I have no this function. What might be problem? I think, that CWinThread modHolder should terminate automatically. Am I wrong?
CWinThread::ExitInstance is called when CWinThread::Run finishes.
You have the source code!

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