I've got some unmanaged code sitting in a DLL. It publishes some methods that my calling (managed) code uses to hook into some COM notifications. Rather than deal with unmanaged code calling back into managed code, I've created a hidden Control derived object and am passing its handle property which the unmanaged code then uses as a parameter to SendMessage.
My Control derived class:
class InteropWindow : Control
{
//delegate
private Handler m_callback;
//window message
private uint m_message;
public InteropWindow(Handler callback, uint message)
: base()
{
m_callback = callback;
m_message = message;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == m_message)
{
m_callback(new IntPtr((int)m.WParam));
}
base.WndProc(ref m);
}
}
Relevant line in unmanaged code:
SendMessage(m_notify, m_window_message, (WPARAM)pData, 0);
m_window_message & m_message are the same (both from RegisterWindowMessage), and m_notify == InteropWindow.Handle (pData varies, but is used as an opaque handle in the managed code). The unmanaged code is being invoked. These facts have been confirmed via debugging.
Shortly after I create the InteropWindow, the calls to SendMessage succeed. Afterwards (seconds later) the messages stop getting to WndProc, though there is no indication of any error.
The question is, what am I doing wrong here?
I've ruled out lifecycle issues (to the best of knowledge anyway), and played with HandleRef to no avail.
Edit the second.
I've re-written this to use function calls instead, which while fraught with its own perils, works a bit more like I'd expect. I've come to suspect this is a COM threading issue, but that's just a gut feeling.
Did you try passing your managed window's handle as a HandleRef? C# can marshal a HandleRef as an IntPtr and vice versa, I've seen Microsoft use that trick quite a bit when decompiling some of their stuff.
You can also load up a .Net profiler and watch the GC. It would be nice to know if your app is breaking right after a collect.
Related
I'am trying to call managed function inside CommandBuffer via IssuePluginEventAndData. It accepts (void* function pointer, int eventId, void *data).
Here's the function:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public unsafe delegate void PluginDelegate(int eventId, void* data);
[MonoPInvokeCallback(typeof(PluginDelegate))]
private static unsafe void MakeGraphicsCallback(int eventId, void* data)
{
//Completely empty.
}
Then store delegate inside non-static MonoBehaviour class and add it to CommandBuffer:
//Prevent delegate to be garbage collected.
private static PluginDelegate makeCallbackDelegate;
public void Start()
{
makeCallbackDelegate = MakeGraphicsCallback;
cmdBuffer.IssuePluginEventAndData(
Marshal.GetFunctionPointerForDelegate(makeCallbackDelegate),
0, IntPtr.Zero);
}
Everything works fine (even if function is not empty), but then, when game is stopped, and runned again it hang on domain reload, here's how editor log ends:
Reloading assemblies for play mode.
Begin MonoManager ReloadAssembly
Then goes nothing, and the only way to make editor work again is to restart it.
I've also tried to call this function from my C++ native plugin function, and also tried to call it from C++ with different calling conventions (cdecl and stdcall explicitly stated in typedef, changed accordingly for UnamangedFunctionPointerAttribute):
typedef void (__stdcall *PluginCallback)(int32_t eventId, void *data);
auto func = static_cast<PluginCallback>((void*)funcPtr);
func((int32_t)eventIdValue, (void*)dataValue);
Result is always the same.
When function is called from main thread -- everything goes fine, but once it called from another thread (unmanaged) by pointer -- assembly reload hangs forever.
Ok, I found the solution. The thing is that if function pointer obtained by GetFunctionPointerForDelegate is called from non-managed thread, you need to first initialize a thread with mono_attach_thread(domain).
So before calling function by pointer, you need to somehow call mono_attach_thread before, as stated in Mono Embedding Guide.
If your application creates threads on its own, and you want them to be able to interact with Mono, you have to register the thread so that the runtime knows about it.
To do so, call the mono_thread_attach() function before you execute any other Mono API or before manipulating any managed object.
Here's my workaround native plugin for this.
https://github.com/Meetem/ManagedRenderEvent
I'm by no means a windows message expert - so forgive me if I'm asking something stupid, but I wasn't able to help myself:
I have a form hooking up to Windows Messages by overriding WndProc to get notified when usb devices are connected or removed (WM_DEVICECHANGE). Everything works fine.
When I moved this code to an underlying user control, I was surprised that it wasn't called any more for WM_DEVICECHANGE messages - other messages do get catched though. Here's the code I used to override.
private const int WM_DEVICECHANGE = 0x0219;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_DEVICECHANGE:
//Device changed - do something
//never passing here when overriding in UserControl
//works when overridden in my main form.
break;
}
base.WndProc(ref m);
}
Further looking for ways to resolve without simply putting the code back in the form, I found the IMessageFilter Interface but implementing and registering it with Application.AddMessageFilter didn't resolve my issue - in fact the WM_DEVICECHANGE message didn't even pass on my PreFilterMessage when I registered my main window - it only passes the WndProc override.
So obviously my understanding of window-messages is far from complete, but what did I miss?
Is there a way to catch WM_DEVICECHANGE without passing by the main window? And to help me for the future: Where would I have to look into to find a good reference which Window Messages get sent where and where not and why?
I have an unmanaged DLL with delphi and integrated there are some events that fire at certain points on runtime.
On its manual it says that it calls the Win32 api PostMessage().
When the event is fired a text message is sent and wParam and IParam have some other information.
How can i fire an event from my c# application when a message from the dll is posted and ofcourse get the information?
UPDATE:
Trying to describe what the manual is saying:
So i have a function called init() which accepts the handle parameter which i am passing as follows:
wr.Init((IntPtr)this.Handle);
from the c# Winform application to the unmanaged dll which returns true if it is fine and that is what i am seeing.
Now the manual says:
When event (from the dll) is fired it sends a windows message to the window handle supplied with the Init function, infact PostMessage() Win32 API is called.
And i have the list of the messages that are sent on the PostMessage() for example:
TextMessage: WM_TECH_BROKENLINE
wParam: Point (which is a number displaying for example where is broken)
IParam: 0
Now as i explained i want that when a message like the one above is posted from the dll i fire an event from c# and ofcourse get the text message and the Params
The steps necessary are as follows:
Obtain a window handle for a window in your C# program.
Pass that window handle to the unmanaged DLL so that it knows where to send the messages.
In your C# code, add code to receive and respond to the message arriving.
As an alternative to the above you might prefer to keep the GUI windows of your program separate from this message passing mechanism. In which case do the following:
Create a subclass of NativeWindow to handle incoming messages.
Create an instance of your class and pass its window handle to the unmanaged DLL.
Override WndProc in your window class and then handle the message there. At which point you can surface it as an event.
This latter approach gives better separation of your interop code from your GUI, and will allow you to make your wrapper less tangled with your GUI.
In order to describe any of this in more detail, one would need to know the details of your program. For instance, is it WinForms or WPF? Does your program have a window at hand to receive messages? How is the string encoded? In which message arg does it arrive? How is memory for the string deallocated if the message is delivered asynchronously?
I suspect you'll have answers to some of these questions, but perhaps not all. You now know at a high level what you need to do, but now need to find out more details for your own program.
Try overriding WndProc method of the form which handle you're passing to the dll. An example can be found here: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.wndproc(v=vs.110).aspx
A simplified example for your case (add it into your form class):
const int WM_TECH_BROKENLINE = 0x0401; // It's WM_USER + 1 in my example.
// Set it according to you dll's spec
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_TECH_BROKENLINE)
{
long dataPassedFromTheDll = (long)m.WParam;
// Your stuff here:
this.Text = string.Format("The dll sent us: {0}", dataPassedFromTheDll);
}
base.WndProc(ref m);
}
I've run into an issue recently where attempting to implement a real-time video capture with a video capture library and capture card. The unmanaged library is much faster at processing the data than I can manually, so it seems the way to go. The unmanaged library is looking for a window handle for a control to update.
The Platform Invoke for the source works fine on a one-off update and when I thread it with my own Invoke, so I know the types are all correct and that it's marshaling properly.
The main issue is that when I attempt to thread the operation and pass the window handle directly to unmanaged code, I get the dreaded "Cross-thread operation not valid" exception.
I understand the problem, just not quite sure how to fix it. So, how do I use a delegate and "BeginInvoke()" method on control when unmanaged code just needs the window handle? Is it even possible to arrange this?
Here's a snippet of the unmanaged library functionality:
class Video
{
[DllImport("video.dll", EntryPoint = "imagePlot", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
private static extern int imagePlot(IntPtr windowHandle, byte[] imageBuffer);
private PictureBox _control;
public Video(PictureBox control)
{
_control = control;
}
public void CaptureAndUpdate()
{
// capture video
byte[] video = Capture();
// plot video
imagePlot(_control.Handle, video);
}
}
You can only change properties on UI elements from the main UI thread. Check this url,
I'm writing a .NET wrapper around an old MFC-based library we have. It's based around a class that sends notifications using window messages; it has a function that lets the user pass in a handle to a window, and that window will receive the messages.
I could just require the users of my wrapper to subclass Control and pass their control's handle in order to receive messages, but that's horrible. I want my wrapper class to have events which fire whenever the old library sends a message, and then I can do the decoding of the message into something sensible. But, I don't want my wrapper class to have to be a control.
Is there a way for me to create a 'dummy' window handle, and receive the messages sent to that handle, without creating a window?
There is a concept of MessageOnly Windows which can help you. You may create an internal message only window in your wrapper class and pass this handle to the old library.
You could try creating a thread with a message pump and sending your messages to that. The thread then raises any necessary events that you want to handle in your C# code.
You can't create a window handle without having a window, since the window handle is the window as far Windows is concerned, but you can make a window without the WS_VISIBLE flag set, and use it for message relaying only. I use that technique sometimes to do cross-thread communication in MFC-only applications (don't tell anyone ;) ). You could derive a (c++) class from CWnd, let it process the messages, and call functions or emit signals for every message that is received. I guess that would make it work with your C# code although I don't have experience with that.
To create a window that does not display, but receives messages, use the following code.
The header file:
#pragma once
#include <afxwin.h>
#define ID_NOTHING_MGS WM_USER + 1001
class control : public CWnd
{
public:
control();
LRESULT control::nothing(WPARAM wp, LPARAM lp);
DECLARE_MESSAGE_MAP()
};
Function implementation in header file.
#include "control.h"
BEGIN_MESSAGE_MAP(control, CWnd)
ON_MESSAGE(ID_NOTHING_MGS, nothing)
END_MESSAGE_MAP()
control::control()
{
// ref1:https://codeantenna.com/a/s6NmJb6YD8
// ref2:https://www.cnblogs.com/greatverve/archive/2012/11/04/mfc-message.html
this->CreateEx(WS_EX_NOACTIVATE, AfxRegisterWndClass(0), _T("windowName"),
WS_CAPTION | WS_OVERLAPPEDWINDOW | WS_EX_NOPARENTNOTIFY,// | WS_VISIBLE,
0, 0, 400, 350, NULL, NULL, NULL);
}
LRESULT control::nothing(WPARAM wp, LPARAM lp)
{
MessageBox(TEXT("control receive message"));
return 0;
}
Since control inherit from the CWnd class, it can receive messages。CreateEx function,creates the specified window and appends it to the CWnd object. If you do not call CreateEx, the window handle value of the created Control object is 0.
No 'WS_VISIBLE' is used, so the window is not visible.