在应用程序中将组件嵌入到主程序集中是一个吸引人的做法,因为它能使部署集更加紧凑。此外,从代码中加载这些图像比散布在文件夹中的图像更不容易出现错误。在开发完整应用程序时,有时需要混合使用C#和VB.NET,这可能是由于最终用户的请求,或者需要与API和/或旧的库调用接口,而这些接口的某些参数特定于某种语言,寻找等效参数可能很麻烦,或者转换根本不起作用。本文涵盖了这两个主题,因为遇到了这些问题,不得不深入研究以获得实际的解决方案。基础知识很简单,但细节需要一些研究和一些额外的工具/步骤,因为微软的文档有点模糊,这没什么新鲜的。
1.1 创建资源组件
首先创建一个资源组件:右键单击解决方案,然后选择弹出菜单中的“添加组件”。这将显示不同的模板,选择一个资源文件:额外的资源项将列在解决方案中,现在单击新添加的资源项。将获得添加资源的选项,请添加图像,在例子中,选择了一些预先准备好的图像。完成后,还会看到在解决方案下创建了一个资源文件夹,图像将列在那里。对于这里展示的方法,请确保每张图像都设置为嵌入式资源。
1.2 从资源组件中检索图像
现在,调用的代码如下所示。突出显示的部分显示了从资源中提取特定图像的语法。这里需要的命名空间是System.Reflection用于Assembly接口,System.IO用于Streaming。
private void btnLoad_Click(object sender, EventArgs e)
{
// 加载资源中的图像
string start_name = "resource_sample";
// 目标EXE(或混合代码模式的DLL)
// 设置基本绘图
displayBuffy = new Bitmap(200, 200);
// 设置屏幕画布
Graphics displayCanvas = Graphics.FromImage(displayBuffy);
Bitmap bmp;
// 现在从资源中以流的形式获取图像
Assembly assembly = Assembly.GetExecutingAssembly();
string strRes = start_name + ".Resources.bandera_checker.gif";
Stream stream = assembly.GetManifestResourceStream(strRes);
bmp = null;
try
{
bmp = new Bitmap(stream);
}
catch { }
stream.Close();
// 在画布上绘制图像
displayCanvas.DrawImage(bmp, 10, 10);
displayCanvas.DrawImage(bmp, new Rectangle(5, 5, bmp.Width / 3, bmp.Height / 3), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
// 在表单上显示
Invalidate();
}
1.3 注意事项和解决方法
到目前为止没什么令人兴奋的。当然,如果只有一两张图像,一切都会非常顺利,但是在构建大型应用程序时,通常需要很多图像,它们在开发过程中通常会被修改。但是,在这样做的过程中,代码最终可能无法加载特定图像,无论进行多深入的调试。要解决这个问题,首先需要双重检查EXE中的图像名称和路径,由于进行了多次更改,有时它要么被移动,要么未包含在内。Visual Studio工具称为ILDASM.exe在这里非常方便,加载EXE(或混合语言解决方案的DLL)将允许看到包含资源的实际路径,只需单击下图所示的MANIFEST,将看到特定图像的实际详细信息,或者它是否确实包含在资源组件中。ILDASM.exe是框架SDK的一部分。如果没有它,可以从这里下载。
有了这个,就可以直接检查特定图像是否确实包含在资源中。众所周知,当代码规模增长并且有很多图像被修改时,它们很快就可能被放错位置或遇到VS中的故障,导致它们被跳过。
正如一开始所述,由于许多原因,可能需要混合代码,有时最终用户有一些较旧的代码,通常是VB.NET,并希望继续使用并维护它,或者有其他类似的原因。为此,可以轻松地添加一个VB.NET部分作为DLL,一切都很好。它可以朝任何方向发展,例如,主要代码在VB.NET中,添加的伴侣代码在C#中,或者反过来。在下面的示例中,C#将是主要代码,伴侣代码将在VB.NET中。
本质上,所需要做的就是向当前解决方案中添加另一个项目。如果默认安装了VS,将看不到添加另一个项目的选项。要启用它,请转到Tools->Options->Projects & Solutions,并确保选中了always show solution,如下图所示:
现在将获得解决方案视图,突出显示解决方案并单击弹出菜单中的Add New Project,在下面选择VB.NET smart_device类型,如下图所示:
在下一个屏幕上,选择类型为class library,就可以开始了。这将使VB.NET部分成为一个DLL,所需的VB.NET例程将居住在那里。
现在项目将在新添加的VB.NET项目下列出class vb文件。在这个文件中,可以放入VB.NET例程库。在现实世界的场景中,有很多例程,如果将具有一些共同行为和/或处理的例程分组到相应的命名空间中,这是有意义的。这样的代码如下所示:
VB.NET
'
所需要做的就是定义小自制命名空间
'
就可以开始混合了
Namespace resource_sample_VB
Public Class VB_Class_1
Public Sub My_add_date(ByRef this_date As Date, ByVal days_2_add As Integer)
Dim tmp_date As Date
tmp_date = this_date
Try
tmp_date = tmp_date.AddDays(days_2_add)
Catch
tmp_date = this_date
End Try
this_date = tmp_date
End Sub
End Class
End Namespace
最后,需要从主项目(C#)调用这个VB.NET代码。由于将VB.NET代码作为类库制作,代码将被编译成DLL。需要这个DLL对主项目可用。为此,需要添加对VB.NET DLL的引用,如下图所示:
这里的一个小问题是,在开始时浏览以引用VB.NETDLL时,将没有这样的DLL,因为VB.NET项目尚未构建。一个快速的解决方法是突出显示vb项目并从构建菜单中构建它,之后可以转到引用并浏览DLL。
最后从C#,这是调用它的语法:
C#
.
.
.
using System.Reflection;
//
需要Assembly
using System.IO;
//
需要Stream
using resource_sample_VB_pieces.resource_sample_VB;
//
来自VB.NET部分
.
.
.
private void btn_Add2Date_Click(object sender, EventArgs e)
{
//
实例化VB.NET类
VB_Class_1 vb_tools = new VB_Class_1();
DateTime thisDate = DateTime.Parse(textDate.Text);
//
调用VB.NET方法
vb_tools.My_add_date(ref thisDate, Int32.Parse(textDays.Text));
//
打印出由VB.NET执行的简单日期计算结果
LblResult.Text = thisDate.ToString("d");
}
2.3 代码的简单输出
在示例代码中,btnLoad_Click从资源组件加载图像,btn_Add2Date_Click按钮将文本框中的值传递给VB.NET,简单的日期计算被打印出来。