目录
使用控件有很多限制,总有些人不喜欢使用winform,习惯用别的工具开发界面。本文在探索一个通用的工程,添加.dll文件实现在任意C#工程中即可调用三菱提供的官方函数。
一.失败案例:
示例工程中,在工程文件下bin目录查看引用的.dll文件,我们初步猜测项目必须引用的库为前两个。
接下来开始实验,我们新建一个控制台应用程序,不同于示例中,控制台项目中没有任何界面或者控件。
在引用中添加上述两个库,即:
添加命名控件,随便写两行代码,验证一下相关函数是否可用,我这里使用的是Open函数。
很遗憾,它在报错,这个报错的大概意思是指有个基类需要在程序集System.Windows.Forms中才能使用,现在缺少此程序集。
还是在刚才的引用中,找到这个程序集并选中,可以发现报错已经消失:
接着运行此代码,很遗憾,还在报错,该报错的大概意思就是ActiveX控件只能在单线程使用,而控制台程序是在多线程下运行的:
百度解决方案,当前线程不在单线程单元中,因此无法实例化 ActiveX 控件解决办法 - winzheng - 博客园 (cnblogs.com),按照此种方法尝试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AxActProgTypeLib;
using AxActUtlTypeLib;
using ActUtlTypeLib;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Thread newThread = new Thread(new ThreadStart(ThreadMethod));
//ApartmentState Property is obsolete;
// newThread.SetApartmentState(ApartmentState.MTA);
// The following line is ignored since
// ApartmentState can only be set once.
newThread.SetApartmentState(ApartmentState.STA);
Console.WriteLine("ThreadState: {0}, ApartmentState: {1}",
newThread.ThreadState, newThread.ApartmentState);
newThread.Start();
// Wait for newThread to start and go to sleep.
Thread.Sleep(300);
try
{
// This causes an exception since newThread is sleeping.
newThread.SetApartmentState(ApartmentState.STA);
}
catch (ThreadStateException stateException)
{
Console.WriteLine("\n{0} caught:\n" +
"Thread is not in the Unstarted or Running state.",
stateException.GetType().Name);
Console.WriteLine("ThreadState: {0}, ApartmentState: {1}",
newThread.ThreadState, newThread.GetApartmentState());
}
}
static void ThreadMethod()
{
// Thread.Sleep(1000);
AxActUtlType axActUtlType1 = new AxActUtlType();
axActUtlType1.ActLogicalStationNumber = 1;
//Set the value of 'Password'.
//The Open method is executed.
int iReturnCode = axActUtlType1.Open();
Console.WriteLine(iReturnCode);
}
}
}
依然在报错:
于是博主参照示例工程的引用项目,将没有的程序集都添加了进来:
最终还在报和上述一样的错误,可以看出这个AxHost只能在winform下运行,此次尝试失败。
二.成功案例:
接下来我们转变思路,经过我们上文中的做法,已经可以确定该控件必须在winform环境下运行,所以接下来可以尝试一下在控制台程序中内嵌一个窗体,类似Windows系统中内嵌一个Linux虚拟机。
新建一个工程,在“解决方案-引用”中添加以下三个库:
有些库的复制本地属性是False,意思就是直接添加不会被导入本地文件夹,在引用中添加后还需要手动将该文件复制进入工程文件中的bin文件,上述三个库并不需要,但是后续可能有的会需要,提前说明避免踩坑。
同样的,记得添加命名空间:
下边粘贴代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AxActUtlTypeLib;
using System.Threading;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
Thread newThread = new Thread(new ThreadStart(ThreadMethod));
//ApartmentState Property is obsolete;
// newThread.SetApartmentState(ApartmentState.MTA);
// The following line is ignored since
// ApartmentState can only be set once.
newThread.SetApartmentState(ApartmentState.STA);
Console.WriteLine("ThreadState: {0}, ApartmentState: {1}",
newThread.ThreadState, newThread.ApartmentState);
newThread.Start();
// Wait for newThread to start and go to sleep.
Thread.Sleep(300);
try
{
// This causes an exception since newThread is sleeping.
newThread.SetApartmentState(ApartmentState.STA);
}
catch (ThreadStateException stateException)
{
Console.WriteLine("\n{0} caught:\n" +
"Thread is not in the Unstarted or Running state.",
stateException.GetType().Name);
Console.WriteLine("ThreadState: {0}, ApartmentState: {1}",
newThread.ThreadState, newThread.GetApartmentState());
}
void ThreadMethod()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainOperation formTest = new MainOperation();
int iReturnCode;
iReturnCode = formTest.test();
Console.WriteLine(iReturnCode);
Application.Run(formTest);
}
}
}
public partial class MainOperation : Form
{
public MainOperation()
{
}
public int test()
{
AxActUtlTypeLib.AxActUtlType axActUtlType1 = new
AxActUtlTypeLib.AxActUtlType();
this.Controls.Add(axActUtlType1);
axActUtlType1.ActLogicalStationNumber = 1;
int iReturnCode = axActUtlType1.Open();
return iReturnCode;
}
}
}
这个代码就是在单线程运行的基础上,建立一个Form,控件实际还是在form中运行,博主在尝试时曾提示过运行环境错误,故将管理器配置为X86,若无相关报错则无需理会:
运行结果如下:
相同的问题,既然已经在form环境中运行控件了为什么还会报错?不知道大家记不记得前边文章中使用控件前需要勾选工具箱中组件,而这里我们是用的是控制台,根本没有选择组件这一说,就相当于我们使用的自定义组件没有注册:
再次尝试,我们事先已经配置并启动Communication Setup Utility,启动Simulator 3,再次启动:
返回值为0,证明已经成功,下边粘贴修改后的Test函数代码:
public int test()
{
AxActUtlTypeLib.AxActUtlType axActUtlType1 = new
AxActUtlTypeLib.AxActUtlType();
((System.ComponentModel.ISupportInitialize)(axActUtlType1)).BeginInit();
this.Controls.Add(axActUtlType1);
((System.ComponentModel.ISupportInitialize)(axActUtlType1)).EndInit();
axActUtlType1.ActLogicalStationNumber = 1;
int iReturnCode = axActUtlType1.Open();
return iReturnCode;
}
结论
本质上还是在Winform环境下调用的.dll,因为我们并没有看到三菱.dll库的底层,所以最终尝试的结果还是证明在winform控制台下调用库才能稳定运行。。