使用 Mono API 启动新线程并打开对话框时出错
我正在开发一个用 C++ 编写的 Native dll,它使用 mono 来显示图形用户界面。我编写了一个简单的框架,它可以工作,但在某些条件下会出现错误。
首先是我从 Mono API 调用的 C# 代码,
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
namespace testApp
{
static class Program
{
// This creates a new thread and runs dialog() on it,
// which opens a dialog window
static void start()
{
Console.WriteLine("Running thread in Mono");
Thread t = new Thread(new ThreadStart(dialog));
t.Start();
}
public static void dialog()
{
Form f = new Form();
f.ShowDialog();
f.Dispose();
}
}
}
它被编译为 testApp.dll
在我的 C/C++ 代码中,我执行以下操作:
- 加载程序集
- 找到 start() 方法并运行它
- 进入一个无限循环,该循环读取 从控制台输入
- 当收到输入字符串“open”时
,再次运行 start()现在,在开始时,表单打开,它可以工作(不会在屏幕上冻结,因为它在自己的线程中运行),我可以打开更多通过键入“open”来打开表单的实例在提示符下。 仅当我关闭所有打开的表单然后尝试打开一个新表单(关闭所有打开的表单后再次键入“open”)时,才会引发异常。
Unhandled Exception: System.OutOfMemoryException: Not enough memory to complete operation [GDI+ status: OutOfMemory]
at System.Drawing.GDIPlus.CheckStatus (Status status) [0x00000] in <filename unknown>:0
at System.Drawing.Graphics.FromHwnd (IntPtr hwnd) [0x00000] in <filename unknown>:0
at System.Windows.Forms.XplatUIWin32.GetAutoScaleSize (System.Drawing.Font font) [0x00000] in <filename unknown>:0
at System.Windows.Forms.XplatUI.GetAutoScaleSize (System.Drawing.Font font) [0x00000] in <filename unknown>:0
at System.Windows.Forms.Form.GetAutoScaleSize (System.Drawing.Font font) [0x00000] in <filename unknown>:0
at System.Windows.Forms.Form..ctor () [0x00000] in <filename unknown>:0
at (wrapper remoting-invoke-with-check) System.Windows.Forms.Form:.ctor ()
at testApp.Program.dialog () [0x00000] in <filename unknown>:0
at System.Threading.Thread.StartUnsafe () [0x00000] in <filename unknown>:0
你能帮我解密该消息吗? :)
不知何故,当我关闭最后一个表单窗口时,我猜单声道决定卸载/关闭一些关键组件,这些组件阻止我在该时间点之后打开另一个窗口。
这是我使用的 C++(实际上是 C)代码:
#define _CRT_SECURE_NO_WARNINGS
#include <mono/jit/jit.h>
#include <mono/metadata/object.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static void runThread(MonoDomain* domain, MonoAssembly* assembly)
{
MonoImage* image = mono_assembly_get_image (assembly);
MonoClass *klass;
MonoObject *obj;
MonoMethod *m = NULL, *start = NULL;
void* iter = NULL;
klass = mono_class_from_name (image, "testApp", "Program");
// Find method start()
while ((m = mono_class_get_methods (klass, &iter)))
{
if (strcmp (mono_method_get_name (m), "start") == 0)
{
start = m;
break;
}
}
mono_runtime_invoke (start, NULL, NULL, NULL);
}
int main(int argc, char* argv[])
{
MonoDomain *domain;
const char *file;
int retval;
if (argc < 2)
{
fprintf (stderr, "Please provide an assembly to load\n");
return 1;
}
file = argv [1];
domain = mono_jit_init (file);
MonoAssembly *assembly;
assembly = mono_domain_assembly_open (domain, file);
if (!assembly)
{
printf("Can not load assembly");
exit (2);
}
// open dialog
runThread(domain, assembly);
// endless loop
char *p = new char[100];
while(1)
{
gets (p);
// Open another dialog
if( strcmp(p, "open") == 0)
runThread(domain, assembly);
}
retval = mono_environment_exitcode_get ();
mono_jit_cleanup (domain);
return retval;
}
I'm working on a Native dll written in C++ that uses mono to show a graphical user interface. I've written a simple skeleton, it works but I get an error under certain conditions.
First here is the C# code I'm calling from the Mono API
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
namespace testApp
{
static class Program
{
// This creates a new thread and runs dialog() on it,
// which opens a dialog window
static void start()
{
Console.WriteLine("Running thread in Mono");
Thread t = new Thread(new ThreadStart(dialog));
t.Start();
}
public static void dialog()
{
Form f = new Form();
f.ShowDialog();
f.Dispose();
}
}
}
This gets compiled as testApp.dll
In my C/C++ code I do the following:
- Load the assembly
- Locate the start() method and run it
- enter an endless loop that reads input from console
- when the input string "open" is received, run start() again
Now, at the beginning, the form opens, it works (doesn't freeze on screen as it's running in its own thread) and I can open more instances of the form by typing "open" at the prompt.
An exception is thrown only when I close all open forms and then attempt to open a new one (typing "open" again after closing ALL opened forms).
Unhandled Exception: System.OutOfMemoryException: Not enough memory to complete operation [GDI+ status: OutOfMemory]
at System.Drawing.GDIPlus.CheckStatus (Status status) [0x00000] in <filename unknown>:0
at System.Drawing.Graphics.FromHwnd (IntPtr hwnd) [0x00000] in <filename unknown>:0
at System.Windows.Forms.XplatUIWin32.GetAutoScaleSize (System.Drawing.Font font) [0x00000] in <filename unknown>:0
at System.Windows.Forms.XplatUI.GetAutoScaleSize (System.Drawing.Font font) [0x00000] in <filename unknown>:0
at System.Windows.Forms.Form.GetAutoScaleSize (System.Drawing.Font font) [0x00000] in <filename unknown>:0
at System.Windows.Forms.Form..ctor () [0x00000] in <filename unknown>:0
at (wrapper remoting-invoke-with-check) System.Windows.Forms.Form:.ctor ()
at testApp.Program.dialog () [0x00000] in <filename unknown>:0
at System.Threading.Thread.StartUnsafe () [0x00000] in <filename unknown>:0
Can you help me decrypt that message? :)
somehow, when I close the last form window, I guess mono decides to unload/shut down some critical component that prevents me from opening another window after that point in time.
Here is my C++ (well, C actually) code that I use:
#define _CRT_SECURE_NO_WARNINGS
#include <mono/jit/jit.h>
#include <mono/metadata/object.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static void runThread(MonoDomain* domain, MonoAssembly* assembly)
{
MonoImage* image = mono_assembly_get_image (assembly);
MonoClass *klass;
MonoObject *obj;
MonoMethod *m = NULL, *start = NULL;
void* iter = NULL;
klass = mono_class_from_name (image, "testApp", "Program");
// Find method start()
while ((m = mono_class_get_methods (klass, &iter)))
{
if (strcmp (mono_method_get_name (m), "start") == 0)
{
start = m;
break;
}
}
mono_runtime_invoke (start, NULL, NULL, NULL);
}
int main(int argc, char* argv[])
{
MonoDomain *domain;
const char *file;
int retval;
if (argc < 2)
{
fprintf (stderr, "Please provide an assembly to load\n");
return 1;
}
file = argv [1];
domain = mono_jit_init (file);
MonoAssembly *assembly;
assembly = mono_domain_assembly_open (domain, file);
if (!assembly)
{
printf("Can not load assembly");
exit (2);
}
// open dialog
runThread(domain, assembly);
// endless loop
char *p = new char[100];
while(1)
{
gets (p);
// Open another dialog
if( strcmp(p, "open") == 0)
runThread(domain, assembly);
}
retval = mono_environment_exitcode_get ();
mono_jit_cleanup (domain);
return retval;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
经过更多研究后,我可以有把握地说这是 Mono 中的一个错误。我已经提交了关于此事的错误报告。
对于 C API,这不是问题,因为我只能在 C# 代码中重新创建该错误。当第一次在新线程上打开表单时,Mono 似乎做了一些初始化工作。如果该线程停止运行或耗尽,那么任何内容的一些引用都会丢失,并且此后打开表单或对话框的任何调用都将失败。这是意外且不受欢迎的行为。
AFter researching this a lot more I can safely say that this is a bug in Mono. I've submitted a bug report on the matter.
This was not a problem regarding the C API as I was able to recreate the bug in only C# code. When opening a form for the first time on a new thread Mono seems to do some initialization work. If that thread stops running or runs out, then some reference of whatever is lost, and any calls that open forms or dialogs after that will fail. This is unexpected and unwanted behavior.