我们有一个VB6应用程序,我们正在迁移到.NET。我正在调用VB6中的.NET函数打开一个WPF窗口。我们经常需要在主线程上运行这些窗口,因此它的行为就像另一个VB6 Form。我们还需要从这些新窗口运行异步操作,因此需要Dispatcher和SynchronizationContext。当我们第一次创建调用WPF窗口的服务时,我们只需启动一个新的Dispatcher和DispatcherSynchronizationContext。
有一次,我打开了一个WPF窗口,它没有绘制边框。 是否存在使用上述方法我不知道线程问题的可能性?这似乎是解决这个问题的正确方法吗?我很害怕这种方法存在竞争条件或隐藏的问题,因为我们自己管理调度程序不像典型的.net应用程序。
我知道有Interop Forms Toolkit,但我们不需要vb6代码直接与表单进行交互,因此看起来有点过分和繁琐。我们刚刚开始编写这些窗口和这个界面,所以很高兴知道我的方法是否早晚出错。
答案 0 :(得分:1)
您必须为WPF窗口提供COM接口(ActiveX用户控件),以显示.tlb和.idl文件。
为此,我通常会创建一个单独的项目,仅关注此接口并引用您的WPF项目。添加一个或多个可以由VB6程序实例化的类,根据需要提供所有GUID和接口描述。
使用此项目,使用项目构建属性中的“注册COM Interop”选项,或者(我的偏好)在项目的构建事件脚本中使用 regasm 来创建.tlb和.idl
部署:您的WPF DLL,.tlb和.idl。 然后从VB6项目中引用此类型库,根据需要创建和使用对象。
我建议你管理WPF和COM接口代码中的所有同步,因为带有COM的VB6往往是STA。
希望这会有所帮助:)
答案 1 :(得分:0)
另一种选择是将WPF窗口构建为单独的进程,并从VB6中调用它们。 这是一个关于如何执行此操作的线程: What is the VB 6 equivalent of Process.Start?
同样,特别是如果您不需要VB6的任何交互,您应该管理WPF exe中的所有线程。
答案 2 :(得分:0)
// COM visible interfaces
[ComVisible(true)]
[Guid("..."), InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IWpfCtl
{
void Show();
void Close();
void ComMethod(string key);
}
[ComVisible(true)]
[Guid("..."), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IWpfCtlEvents
{
void OnTestButton(string message);
}
// Implementation
public delegate void TestButtonDelegate(string message);
[ComVisible(true)]
[Guid("..."), ComSourceInterfaces(typeof(IWpfCtlEvents)), ClassInterface(ClassInterfaceType.None)]
public class WpfCtl : IWpfCtl, IDisposable
{
Thread _windowThread;
WpfWindow _wpfWindow;
public event TestButtonDelegate OnTestButton;
// Default parameterless constructor
public WpfCtl()
{
}
// Instantiate and show a WPF Window
public void Show()
{
// create a thread
_windowThread = new Thread(new ThreadStart(() =>
{
// create synchronization context
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
// instantiate the window
_wpfWindow = new WpfWindow();
// shut down the dispatcher on window close
_wpfWindow.Closed += (s, e) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
// bind to window events
_wpfWindow.testButton.Click += (sender, args) => OnTestButtonClick("Test button was clicked");
// show the window
_wpfWindow.Show();
// start the dispatcher processing
System.Windows.Threading.Dispatcher.Run();
}));
// set the apartment state
_windowThread.SetApartmentState(ApartmentState.STA);
// make the thread a background thread
_windowThread.IsBackground = true;
// start the thread
_windowThread.Start();
}
public void Dispose()
{
Close();
}
// Close window
public void Close()
{
if (_windowThread != null && _windowThread.IsAlive && _wpfWindow != null)
{
if (_wpfWindow.Dispatcher.CheckAccess())
_wpfWindow.Close();
else
{
_wpfWindow.Dispatcher.Invoke(() => { _wpfWindow.Close(); });
}
}
}
// COM visible method
public void ComMethod(string key)
{
if (_wpfWindow != null)
_wpfWindow.Dispatcher.Invoke(() => { _wpfWindow.Method(key); });
}
// COM event
private void OnTestButtonClick(string message)
{
if (OnTestButton != null)
{
OnTestButton.Invoke(message);
}
}
}