windows 编译版本异常处理
2013-05-30 18:26 chenkai 阅读(5722) 评论(7) 编辑 收藏 举报今天在编译一个Windows Form程序时.突然发现一个问题.在本机开发环境[X64位]能够很好调用.等打包安装后放到32位机器是突然发现提示一个错误如下:
具体异常信息如下:
有关调用实时(JIT)调试而不是此对话框的详细信息,
请参见此消息的结尾。************** 异常文本 **************
System.DllNotFoundException: 无法加载 DLL“Win32Project1.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)。
在 AndroidSkinTool.UnsafeNativeMethods.getKey()
在 AndroidSkinTool.Forms.FormMain.btnRefresh_Click(Object sender, EventArgs e) 位置 d:\C#Workspace\SkinToolCode_V6.1\Forms\FormMain.cs:行号 2708
在 System.Windows.Forms.Control.OnClick(EventArgs e)
在 System.Windows.Forms.Button.OnClick(EventArgs e)
在 System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
在 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
在 System.Windows.Forms.Control.WndProc(Message& m)
在 System.Windows.Forms.ButtonBase.WndProc(Message& m)
在 System.Windows.Forms.Button.WndProc(Message& m)
在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
其实在应用程序中实现一个很简单的功能. 在。net程序中C#通过Dllimport方式来调用C++ DLL库.传递一个字符串而已. 而出现System.DllNotFoundException异常的原因主要存在如下几个问题:
首先需要确定调用C++ DLL是否正确导出.因为可以再开发环境正常运行.可见DLL已经成功导出.成功导入后.需要确认在.NET环境导入的方式是否正确.在导入前需要引用如下库:
1: using System.Runtime.InteropServices;
导出方法:
1: [DllImport("WindowsEntity1.dll",EntryPoint= "TA_Init3",CallingConvention = CallingConvention.StdCall)]
2: public static extern int TA_Init3(string IP, Int16 port,ushort SysCode, ushort TerminalNo);
如果导出方式也正确.还提示如上问题的话.这个时候我们则需要进一步分析调用C++DLL 有没有引用其他库或存在其他依赖.采用dependency worker工具分析当前windowsEntity1 DLL引用依赖项 如下:
you see.可以看到当前win32Project1.DLL主要有两个依赖项 MSVCR110.DLL和KEREML32.DLL. 而MSVCR110在当前操作系统确实才导致了当前开始运行提示DLLNotFoundException异常信息.我们可以再项目Realse目录下添加改DLL或是把改DLL拷贝到System 32下保证系统环境中可见. 发现DLLNotFoundEXception异常则消失了.应用程序正常运行.这里需要注意一点. 在通过.NET 调用C++ DLL 时. 应用会首先在System 32系统环境查找是否具有MSVCR110.DLL依赖项.如果没有则继续在安装目录下寻找.如果两边都没有找到则会爆出DllNotFoundException异常信息. 因此简单方式有两种. 第一可以通过代码在调用注册该DLL信息. 另外可以直接把依赖的MSVCR110.DLL拷贝到realse目录下即可.
另外除了以上情况.还有一种特殊情况是.看运行环境是否安装了VC Runtime library. 类似在VS2012 环境下默认是安装的. 有些机器没有安装或是采用VC6.0编译的DLL.所以为了免去打包安装的麻烦.可以通过安装相应VC运行时库或者直接把几个相应版本运行时的库文件直接放到可执行程序里面进行发布,从而省却安装过程. 这是一个比较直接的解决方案.
为了保证能够兼容32和64 两个平台.我在调用.NET程序设置编译选项为AnyCpu方式.却发现。net抛出如下异常信息:
Unhandled Exception:
System.BadImageFormatException: Could not load file or assembly 'SourceCode.HostClientAPI, Version=4.0.0.0, Culture=neutral, PublicKeyToken
=16a2c5aaaa1b130d' or one of its dependencies. An attempt was made to load a program with an incorrect format.
File name: 'SourceCode.HostClientAPI, Version=4.0.0.0, Culture=neutral, PublicKeyToken=16a2c5aaaa1b130d'
也就是你现在看到的System.BadImageFormatException异常.针对这个问题.官方MSDN上说的很详细.Visual Studio的编译选项 build中的platform存在有3个选项X64、Any CPU和x86。X86表示只能在32位环境下运行,X64表示只能在64位环境下运行,Any CPU表示你的程序集可以根据环境变化适应32位还是64位,但是如果你的程序集依赖于一个x86选项编译的程序集类似调用一个win32 C++ DLL,哪么你的程序集只能选择X86进行编译,而不能选择Any CPU编译,如果使用Any CPU编译.则这个时候会抛出System.BadImageFormatException异常信息.
而真正的原因是.如果你采用Anycpu方式进行编译.那么在64位机器默认的运行就是64位的,通常这情况是assembly需要调用一个32的COM库,只要用x86编译,那么程序就是以32位的程序,依靠wow64来运行的,就可以在64位系统上运行。所以,如果assembly需要使用win32库的话。正确的做法是把编译的选项设置X86来编译.即可.唯一点需求.保证调用的C++ DLL编译版本和调用版本一致才是原则所在.这样一来在32和64位则不会爆出如上异常问题.
在官方文档说了两个意外情况. 也是值得参考的.
如果您的应用程序使用了 32 位组件,请确保该应用程序始终采用 32 位应用程序的运行方式。
如果应用程序项目的“平台目标”属性设置为 AnyCPU,则编译后的应用程序在 64 位或 32 位模式中均可运行。如果采用 64 位应用程序运行方式,则实时 (JIT) 编译器便会生成 64 位本机代码。如果应用程序依赖于某个 32 位托管组件或非托管组件,则在 64 位模式中无法加载该组件。若要纠正此问题,请将项目的“平台目标”属性设置为 x86,然后重新编译。
确保未使用利用其他 .NET Framework 版本创建的组件。
如果使用 .NET Framework 1.0 或 .NET Framework 1.1 开发的应用程序或组件尝试加载使用 .NET Framework 2.0 SP1 或更高版本开发的程序集,或者使用 .NET Framework 2.0 SP1 或 .NET Framework 3.5 开发的应用程序尝试加载使用 .NET Framework 4 开发的程序集,便会引发此异常。 BadImageFormatException 异常可能会报告作为编译时错误,或在运行时可能会引发该异常。有关示例,请参见 BadImageFormatException 类
参考资料: