为C# as 类型转换及Assembly.LoadFrom埋坑!

背景:

不久前,我发布了一个调试工具:发布:.NET开发人员必备的可视化调试工具(你值的拥有)

效果是这样的:

之后,有小部分用户反映,工具用不了(没反应或有异常)~~~

然后,建议小部分用户换个电脑环境试试,有些就好了~~~

于是,我假定是VS环境下的 Microsoft.VisualStudio.DebuggerVisualizers.dll 的版本不一致引发的。

因此,一般我都建议用户自己下载源码,重新引用去编绎一下!!!

由于该工具一直在CSDN论坛的VB.NET版块置顶着。

考虑到受众多,中间还偷偷升级过几回,解决了抛异常的问题,不过仍没有从根本性解决~~~~

这两天,有个叫子寒的同学,找上了我,希望我帮他解决这个问题。

我试着重新编绎了新版本发给他,都反馈木有效果。

只好让他下载源码,并在他电脑上进行远程调试。

昨晚处理到深夜1点半,终于:把发现的两个坑给埋了!!!

下面介绍下这两个坑:

1:as 转换的坑:

先看一段源码,这是拿到反序列化的结果,转Table,再绑定:

 protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
        {
            MDataTable dt = objectProvider.GetObject() as MDataTable;
            FormCreate.BindTable(windowService, dt, null);
        }

在这段代码中,调试的结果:

1:objectProvider.GetObject() 拿到的对象是MDataTable,GetType也返回的CYQ.Data.Table.MDataTable。

2:as MDataTable 却返回了null ?

咦?一个大大的问号在我面前,同样的类型,怎么as不过去?

于是我把代码改了一下:

MDataTable dt=(MDataTable)objectProvider.GetObject()

抛异常了:


1

2

3

4

************** 异常文本 **************

System.InvalidCastException: [A]CYQ.Data.Table.MDataTable 无法强制转换为 [B]CYQ.Data.Table.MDataTable。<br>类型 A 源自“CYQ.Data, Version=5.7.5.5, Culture=neutral, PublicKeyToken=null”(在字节数组的上下文“LoadNeither”中)。<br>类型 B 源自“CYQ.Data, Version=5.7.5.5, Culture=neutral, PublicKeyToken=null”(在上下文“LoadFrom”中的“C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Packages\Debugger\Visualizers\CYQ.Data.dll”位置处)。

   在 CYQ.Visualizer.MDataTableVisualizer.Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)

   在 Microsoft.VisualStudio.DebuggerVisualizers.DebugViewerShim.ManagedShim.DelegatedHost.CreateViewer(IntPtr hwnd, HostServicesHelper hsh, SafeProxyWrapper proxy)

这个异常是什么等会再说,先补充知识点先:

1:as 类型转换:只检测上下文中类型是否一致(或存在隐式转换),若失败返回null,不抛异常。

2:强制类型转换:尝试进行类型转换,转换失败时,抛出异常。

好吧,第一个坑,相同的类型,没有异常,埋的够深!!!

AS叫了:这坑不能怪我,要怪就怪Assembly.LoadFrom,谁让你们把我们分隔在不同的上下文中。

2:Assembly.LoadFrom 的坑

这里再贴一段详细的异常信息:

 ************** 已加载的程序集 **************

 异常里核心的两段:

CYQ.Visualizer
    程序集版本:2.0.0.5
    Win32 版本:2.0.0.5
    基本代码:file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio%2012.0/Common7/Packages/Debugger/Visualizers/CYQ.Visualizer.dll
----------------------------------------
CYQ.Data
    程序集版本:5.7.5.5
    Win32 版本:5.7.5.5
    基本代码:file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio%2012.0/Common7/Packages/Debugger/Visualizers/CYQ.Data.DLL

CYQ.Data
    程序集版本:5.7.5.5
    Win32 版本:12.0.21005.1
    基本代码:file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.DebuggerVisualizers/12.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.DebuggerVisualizers.dll

从上面的异常错误信息中,第一眼看是要蒙B的,毕竟少见多怪啊~~~~

大概花了8分钟,终于反应过来了,经过仔细的思考,可以发现:


1

2

3

1:CYQ.Data 被CYQ.Visualizer.dll加载是在同一目录下。

 

2:被Microsoft.VisualStudio.DebuggerVisualizers.dll加载却是在(上下文“LoadNeither”中,鬼知道这个LoadNeither是什么)。

于是,同一个dll被加载成两个不同路径,导致上下文环境不一样,而不能互转~~~~

为什么大部分正常,小部分环境会这么处理呢,目前无从知道,微软也是爱造坑的~~~

好吧,坑已经知道了,接下来如何埋才是重点:

1:埋坑思维一:测试下Assembly.Load、Assembly.LoadFile、Assembly.LoadFrom

写了几段测试代码:

 string filePath = AppDomain.CurrentDomain.BaseDirectory + "cyq\\CYQ.Data.dll";
            byte[] bytes = File.ReadAllBytes(filePath);
            Assembly ass = Assembly.Load(bytes);
            object o = ass.CreateInstance("CYQ.Data.Table.MDataTable");
 MDataTable dt = (MDataTable)o;

失败!

 string filePath = AppDomain.CurrentDomain.BaseDirectory + "cyq\\CYQ.Data.dll";
 Assembly ass = Assembly.LoadFrom(filePath);
 Assembly.LoadFile(filePath);
...省略...

都失败!!

测试这三个方法,主要是想看看有没有啥本质的不一样,特别是和强签名合在一起后~~~

把dll加个强签名试试~~~

仍失败~~~~

看来,我想多了,不同的dll路径加载的对象转换这条路是不通的了~~~~

2:埋坑思维二:强签名DLL,并注册到GAC中

理论上来说:该方式100%是可行的,毕竟路径的引用都是一致的。

方式也简单:通过代码加个注册的命令也不是什么难事~~~~

还是思考有没有其它更简洁的方式!

3:埋坑思维三:找个稳定的中介

既然问题出现在序列化前的MDataTable和反序列化后的MDataTable在不同上下文的dll而导致的。

那就就找一种第三方中介了:MDataTable=>中介(序列化)=》中介(反序列化)=>MDataTable

不用动脑,就可以想到两种:json或 DataTable。

于是代码就动起来了:

序列化时,用DataTable

反序列化时:

打完收工,重新编绎,发给用户测试,正常通过~

工具下载地址:

http://www.cnblogs.com/cyq1162/p/6027051.html

总结:

1:这么多年,第一次栽坑在as转换上,也许是没想到,也许是万万想不到。

2:不同的路径加载的相同的dll类型无法互转,这么多年终于遇上了,说明上的山多还是会见鬼的。

可既然版本号和签名都一致,又认为签名无法伪造,那为什么不呢?

本文原创发表于博客园,作者为路过秋天,原文链接:http://www.cnblogs.com/cyq1162/p/6212200.html

时间: 2024-09-20 17:50:48

为C# as 类型转换及Assembly.LoadFrom埋坑!的相关文章

Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的区别!

参考: http://www.cnblogs.com/benwu/archive/2009/10/24/1589096.html http://www.cnblogs.com/xuefeng1982/archive/2009/11/09/1598956.html   今天总算弄明白了Assembly.LoadFrom 与Assembly.Load 与 Assembly.LoadFile的一些区别, 以前只是用Assembly.Load来生成实例,现在遇到一个问题,就是从应用程序中来创建窗体, 网

SAP MM 采购申请审批策略配置‘挖坑埋坑&#039;之Item Category

SAP MM 采购申请审批策略配置'挖坑埋坑'之Item Category   在后台配置中,我们可以启用classification来实现采购申请的审批策略.我们可以将PR中的ITEM Category字段值作为决定审批策略的特性之一.   笔者在D项目中,PR的审批是在整单Level进行的,启用了Classification来支持PR的审批.   我创建了如下的特性,   以及如下的分类,   笔者在配置生产订单工序委外采购申请的审批策略的时候,有做如下设置:     众所周知,SAP PR

AppDomain与Assembly的动态加载与卸载简介

为了将问题描述清楚,我们先来看一个例子.在这个例子中,WinForm上有一个按钮,当用户点击这个按钮后,就会装载一个已经存在的Assembly,并且在界面的Label控件上显示出这个Assembly的FullName.对Reflection稍微熟悉一点的朋友都知道,这是非常简单的事情,只需要用Assembly.LoadFile方法获得Assembly,然后用FullName属性来显示即可,比如下面的代码: private void button1_Click(object sender, Eve

C#中通过Assembly类访问程序集信息

C#中通过Assembly类可以访问程序集信息. 1.允许访问给定程序集的元元素,包含可以加载和执行程序集的方法: 2.加载程序集:使用静态方法Assembly.Load(程序集名称)或Assembly.LoadFrom(程序集完整路径名): 3.属性: FullName:程序集显示名称: 3.方法: GetTypes():获取程序集中定义的类型. TestAssembly.cs: view plaincopy to clipboardprint? using System; using Sys

C# Assembly类访问程序集信息_php技巧

C#中通过Assembly类可以访问程序集信息. 1.允许访问给定程序集的元元素,包含可以加载和执行程序集的方法: 2.加载程序集:使用静态方法Assembly.Load(程序集名称)或Assembly.LoadFrom(程序集完整路径名): 3.属性: FullName:程序集显示名称: 3.方法: GetTypes():获取程序集中定义的类型. TestAssembly.cs: view plaincopy to clipboardprint? using System; using Sys

c#利用反射Assembly 对类和成员属性进行操作

protected static void test() { //获取程序集 Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();//Assembly.LoadFrom("test.dll"); //获取模块 Module[] modules = assembly.GetModules(); foreach (Module module in modules) { Console.WriteLine

为什么Assembly.GetType()返回null,求大神指点

问题描述 我有一个程序片断Assemblya=Assembly.LoadFrom("BaiduMusic.dll");Typetype=a.GetType("CMusicSearch.BaiduMusic.MainSearch",false);调试运行,a可以得到{BaiduMusic,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null},不过不知道为什么type一直为nullBaiduMusic.dll中类Mai

AppDomain与Assembly的动态加载与卸载

为了将问题描述清楚,我们先来看一个例子.在这个例子中,WinForm上有一个按钮,当用户点击这个按钮后,就会装载一个已经存在的Assembly,并且在界面的Label控件上显示出这个Assembly的FullName.对Reflection稍微熟悉一点的朋友都知道,这是非常简单的事情,只需要用Assembly.LoadFile方法获得Assembly,然后用FullName属性来显示即可,比如下面的代码: privatevoidbutton1_Click(objectsender,EventAr

NET插件系统之一,开头:MEF的一些疑问和相关思考

      实现可扩展的软件系统是我一直的目标和想法.可扩展性显然属于动态编程的范畴.因此,几个月来我在业余时间会抽空学习插件系统.   我参考了博客园的几篇插件式系统的文章.知道了实现插件系统有以下的核心流程:       1. 定义插件接口,并在各个功能组件中实现这些接口 2. 在主程序中,通过遍历所需目录下的dll文件,查询实现该接口的type,从而通过createInstance方法实现其动态创建,并加入到主程序插件列表.下面的代码展示了该功能. public void GetAllPl