.NET开发个别技术备注

有些是网上的解决方案,有些为个人总结,未注明出处请见谅。

 

1.1     List<string>数组使用

List<string>的contains方法为引用类型判断。

 

1.2     非主线程弹出窗体

  1. 非主线程弹出窗体时应使用ShowDialog方法执行模式窗体,否则会出现假死状态。非UI主线程在返回主UI窗体控件时应使用invoke或begininvoke。
  2. 多线程中显示的窗体尽量使用using,否则如果出现窗体内有引用型参数的传入会无法控制该参数的注销时间,随时引发注销(窗体的dispose方法由.NET回收机制调用)。当多次开启同样线程时这个bug就会出现!

 

1.3     WCF传输超出配额

格式化程序尝试对消息反序列化时引发异常: 尝试对参数 http://tempuri.org/ 进行反序列化时出错: spFile。InnerException 消息是“反序列化对象 属于类型 NL.Database.Wcf.ESqlParameter[] 时出现错误。读取 XML 数据时,超出最大数组长度配额(16384)。通过更改在创建 XML 读取器时所使用的 XmlDictionaryReaderQuotas 对象的 MaxArrayLength 属性,可增加此配额。 第 1 行,位置为 543。”。有关详细信息,请参阅 InnerException。

 

修改服务的config文件

<binding name="WCFDBServicesBinding"

     transferMode="Streamed"

     messageEncoding="Mtom"

     maxReceivedMessageSize="10067108864">

<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="2147483647"

              maxBytesPerRead="4096" maxNameTableCharCount="16384" />

        </binding>

maxArrayLength为关键配置。

 

在Rest实现及调用方式中,需要增加配置,否则调用将出现400及500的Http错误!

<configuration>

         <system.web>

                   <httpRuntime maxRequestLength="102400" />

         </system.web>

………………

1.4     多线程退出

当发现多线程内窗体无法关闭时可能为死循环,即窗体关闭导致线程退出时,线程退出方法中调用了窗体的方法。

 

1.5     多线程死锁问题示例

1、  使用线程锁,如分别在某一个对象的两个及其以上的方法中使用Lock以锁定指定的资源不被改变时,当该对象中有事件需要触发回调用方时,事件的触发应在Lock之外,否则如果调用方在事件函数中调用了该对象的其他还有Lock的方法时将出现死锁。

2、  非UI线程中的返回UI线程时,因注意返回UI的执行过程(即Invoke或BeginInvoke内的执行过程)应不能包含对其他UI线程的调用(即包含Invoke或BeginInvoke内的执行过程)。

1.6     net.tcp的服务IP配置问题

net.tcp的服务IP配置在多层网络结构下应配置为本机的IP,而非最外层外网IP,否则会出现:在其上下文中,该请求的地址无效。" ErrorCode:10049

 

1.7     非托管的调用

1. C# 和 C++ 执行指令的速度 测下来 C# 要慢1倍,这个和微软官方公布的是一致的。对400 万 VInt  读取,C# 用时最快为76ms, C++ 为 34ms

2. 虚拟方法比普通方法速度慢8倍

3. DLLImport win32 动态库,调用win32 函数的速度比直接调用 C# 自身函数速度慢100倍

4. InterLock 比 lock 速度快1倍

5. 用  Marshal.AllocHGlobal 连续申请和释放4M-400M之间大小的内存1000万次,没有发现明显内存碎片产生的迹象。

 

1.8     内存泄露检测总结

微软原型文章链接:http://msdn.microsoft.com/en-us/library/ee658248.aspx

  1. 常见的使用了非托管资源的类如下:

ApplicationContext

Component

ComponentDesigner

Brush

Container

Context

Cursor

FileStream

DataSet

Font

Icon

Image

Matrix

Texture

OdbcDataReader

OleDBDataReader

Pen

Socket

StreamWriter

Timer

Transaction

DataReader

Ping

Tooltip

Bitmap

SerialPort

Tooltrip

ListView

  • The system uses User objects to support window management. They include: Accelerator tables, Carets, Cursors, Hooks, Icons, Menus and Windows.
  • GDI objects support graphics: Bitmaps, Brushes, Device Contexts (DC), Fonts, Memory DCs, Metafiles, Palettes, Pens, Regions, etc.
  • Kernel objects support memory management, process execution, and inter-process communications (IPC): Files, Processes, Threads, Semaphores, Timers, Access tokens, Sockets, etc.

 

以上列出的类均继承了IDisposable接口,需要在使用完后调用Dispose方法释放或者使用Using语句块,比如DataTable、DataSet、DataReader、Transaction、BitMap…

 

  1. 事件造成的内存泄漏

l  当不需要使用事件时,应退订事件,为了确保安全可以在Dispose方法中退订事件.

l  当对象不再触发事件时,应该将对象设为null来移除所有的事件订阅者

事件未退订会导致整体父控件均无法被回收。

  1. 动态添加生成控件造成内存泄漏

对象的创建者或拥有者负责销毁对象,而不是使用者

动态生成引用了非托管资源的控件后,注意一定要Dispose();

         例如:                

RichTextBox rtb = new RichTextBox();

frm.Controls.Add(rtb);

frm.Controls.Remove(rtb);

rtb.Dispose();

此项在【模块化】的程序中异常突出,动态增加的子项必须手动注销,如ListView中的Item及SubItem、ToolStrip的注销

  1. Win32API及COM

指通过本地API函数与托管对象进行交互(比如:通过 P/Invoke方式调用本地DLL,DLLImport声明静态外部函数和COM Interop)所用到的非托管资源。

例如:当通过DLL Import调用 API函数GetDC函数时忘了调用ReleaseDC去释放设备句柄造成4个字节的内存泄漏。

再如:智能文档中使用的Word以及导出EXCEl功能用到的Office的COM非托管组件,在关闭时GC不能识别COM组件而造成有时候无法对COM对象进行释放,这时候可以通过以下两个InteropServices函数进行释放

   System.Runtime.InteropServices.Marshal.ReleaseComObject(comObject);

递减与指定的 COM 对象关联的指定 运行时可调用包装 (RCW) 的引用计数。

返回值为关联的 RCW 的引用计数的新值。此值通常为零,因为无论调用包装 COM 对象的托管客户端有多少,RCW 仅保留对该对象的一次引用。

所以通过这个方法显式的通过CLR释放非托管 COM 对象上的所有引用。

   System.Runtime.InteropServices.Marshal.FinalReleaseComObject(comObject);

通过将 运行时可调用包装 (RCW) 的引用计数设置为 0,释放对它的所有引用。

返回值为与 comObject参数关联的 RCW 的引用计数的新值,如果释放成功,则为 0(零)

 

1.8.1        内存泄漏之MS框架BUG的修复方案

MS框架bug:

l  CodeDomProvider及其继承类库(CsharpCodeProvider、JScriptCodeProvider),bug出现在CompileAssemblyFromSource方法中,执行后dispose无效,内存无法回收。

 

通用的修复方案为使用AppDomain,将有问题的框架调用代码放入另一个程序域中执行,执行完成后卸载该程序域。

示例:

static private object EvaluateAppDomain(ScriptLanguage scriptLanguage, string code)

{

            AppDomainSetup appDomainSetup = new AppDomainSetup();

            appDomainSetup.ApplicationBase = System.Environment.CurrentDirectory;

            appDomainSetup.DisallowBindingRedirects = false;

            appDomainSetup.DisallowCodeDownload = true;

            appDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

            // Create the second AppDomain.

            AppDomain appDomain = AppDomain.CreateDomain("Evaluator Domain", null, appDomainSetup);

            try

            {

//以下为对有bug代码的调用

                // Create an instance of MarshalbyRefType in the second AppDomain.

                // A proxy to the object is returned.

                Evaluator eval = (Evaluator)appDomain.CreateInstanceAndUnwrap(typeof(Evaluator).Assembly.FullName, typeof(Evaluator).FullName);

                // Call a method on the object via the proxy, passing the

                // default AppDomain's friendly name in as a parameter.

                return eval.Evaluate(scriptLanguage, new EvaluatorItem(code));

            }

            finally

            {

                AppDomain.Unload(appDomain);

            }

}

1.9     多线程中Control.CheckForIllegalCrossThreadCalls=false不可随便使用

Control.CheckForIllegalCrossThreadCalls=false不可在多线中随便使用  

CheckForIllegalCrossThreadCalls和control.Invoke有什么不同,哪个更好用,更高效呢?
        占在任何角度讲,都不要使用CheckForIllegalCrossThreadCalls,即便他运行和代码编写的确实比Invoke效率高 。 
        感兴趣的,可以参考我后面贴出的代码来测试对比一下两者的不同。这里我只简单说一下结论:
        1、性能CheckForIllegalCrossThreadCalls=false时比invoke高,而且代码比较优雅。
        测试时间如下:
        Button1 ---------------------------
        00:00:01.0760900
        00:00:01.0771200
        Button2 --------------------------
        00:00:01.0812499
        00:00:01.0813443
        效率差多少?在这里时间还不到1%,代码少写一个if字句
        看到有文章说这种方法在大量更新ui时会引发大量异常,导致性能下降
        我测试了一下,耗时和循环次数是很平稳的线性关系,而且也没有发现几个Exception相关性能计数器有问题,这说明这又是某老外朋友想当然的说法。

        2、CheckForIllegalCrossThreadCalls在.net1.x中默认是false,也就是不检查,.net2.0和3.x默认是true
        说明这是ms有意的引导,说不定以后不让你改了。这也是很多1.x用户在刚用2.0时不习惯跨线程更新ui的原因之一。

        3、死穴:安全性
        CheckForIllegalCrossThreadCalls容许子线呈随时更新ui,在同一个test函数体内,不能保证自身事务的一致性。给label1付了值
        一回头,就已经被别人改了,这和超市的踩踏事件的后果一样严重。
        当然你可以自己加锁,用信号量,这样还不如直接使用Invoke了,你只是又把别人做好的事情做了一遍。

        如果你觉的你的应用不会考虑在写入ui的同时来读取ui,而倾向使用CheckForIllegalCrossThreadCalls来追求效率的话,也是不恰当的做法。
        首先CheckForIllegalCrossThreadCalls并不能让效率发生本质的变化。
        其次需求永远是变化的,现在不考虑不等于以后不会碰到
        听从ms的引导。否则以后要在高版本的.net framework中移植代码的时候需要花费数倍的人工。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // 模拟一个实际应用
        // 对label1付值后立马检查他的值,如果已经被改过则抛出异常

        void test()
        {
            string strText = Guid.NewGuid().ToString();
            this.label1.Text = strText;
        }

        // 不使用invoke方法直接进入Control
        void doThread()
        {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Reset();
            sw.Start();
            for (int i = 0; i < 100; i++)
            {
                test();
                System.Threading.Thread.Sleep(10);
            }
            sw.Stop();
            Console.WriteLine(sw.Elapsed);
        }

        // 使用invoke方法
        public delegate void dTest();
        void invokeThread()
        {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Reset();
            sw.Start();
            for (int i = 0; i < 100; i++)
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(new dTest(test));
                }
                else
                {
                    test();
                }
                System.Threading.Thread.Sleep(10);
            }
            sw.Stop();
            Console.WriteLine(sw.Elapsed);
        }

        // 通过CheckForIllegalCrossThreadCalls的值忽略跨线程错误,这时会抛出异常
        private void button1_Click(object sender, EventArgs e)
        {
            Form1.CheckForIllegalCrossThreadCalls = false;
            new System.Threading.Thread(new System.Threading.ThreadStart(doThread)).Start();
            new System.Threading.Thread(new System.Threading.ThreadStart(doThread)).Start();
        }

        // 通过invoke方法,在主线程排队,lable1的值在test函数体内永远是线程安全的.
        private void button2_Click(object sender, EventArgs e)
        {
            new System.Threading.Thread(new System.Threading.ThreadStart(invokeThread)).Start();
            new System.Threading.Thread(new System.Threading.ThreadStart(invokeThread)).Start();
        }
    }
}

 

1.10        使用.NET向webService传double、int、DateTime 服务器得到的数据时null的问题

用C#.NET调用Java开发的WebService时,先在客户端封装的带有int属性的对象,当将该对象传到服务器端时,服务器端可以得到string类型的属性值,却不能得到int类型、double和DateTime类型的值(在服务端得到的均为null)

解决办法:

VS2005封装WebService引用时,如果WebService发布的是一个基本数据类型组成的对象,则会对该对象的非string属性同时生成两个属性,一般是这样的"属性××"、"属性××Specified"。而"属性名Specified"是一个bool类型,只有这个属性被设置成true时,"属性××"的值才会被序列化成xml传递。



同样的道理,用.NET提供WebService给JAVA调用时,如果是一个基本数据类型组成的对象,则会对该对象的非string属性同时生成两个属性,一般是这样的"属性××"、"属性××Specified"。如果不将“属性××Specified”设置为true的话,服务端得到的数据将是null。解决办法就是把“属性××Specified”设置为true或者把“属性××Specified”删掉。

 

 

1.11             反射导致无法调试(下断点) Assembly.Load与Assembly.LoadFile

Assembly.Load会导致无法调试;

Assembly.LoadFile不会。

我们在使用C# 语言的Assembly.Load 来加载托管程序集并使用反射功能时,一般需要先通过Assembly.Load(), Assembly.LoadFrom() 等方法将目标托管程序集加载到当前应用程序域中,然后生成对应实例,最后再进行调用实例的属性或者方法。

一般情况下,我们调用Assembly.Load 一类方法是不会出问题的,但是对于以下几种情况Assembly.Load 方法无法处理:

  1. 程序集可能是延迟签名的。
  2. 程序集可能被CAS 策略保护。
  3. 宿主程序与目标程序集的处理器架构不同。
  4. 当加载目标程序集时,目标程序集中的方法可能正在运行。 (比如,模块初始化)
  5. 程序集可能应用了绑定策略, 你可能不会得到你想要的那个程序集。

我们现在关注第四种情况,因为这种情况是最常见的。我们思考以下几个问题:

 

1. 为什么目标程序集的方法在运行时不允许再加载一次?

准确地说是为什么在一个应用程序域(AppDomain)中加载后的程序集默认不允许再另外一个应用程序域中加载?这是因为在第一次加载应用程序集时,Assemlby.Load 方法会将此程序集锁住,以防止在自己使用过程中应用程序集被其他应用程序修改(一般指删除)。这其实与Win32 API 中的CreateFile 函数行为类似,我们都知道,在 Windows 中去占用一个文件最直接、最简单的方式就是调用 CreateFile API 函数来打开文件。具体请参照:

http://blog.csdn.net/xt_xiaotian/article/details/6362450 

 

2. Assembly.Load 方法能否实现在加载目标程序集时不锁定它?

我们可以使用如下代码加载我们的程序集:

   1: byte[] buffer = System.IO.File.ReadAllBytes(yourFullfileNamePath);

   2: //Load assembly using byte array

   3: Assembly assembly = Assembly.Load(buffer);

后台的实现是暂时先将目标程序集锁定,然后把程序集内容复制到内存中,读取后将程序集解锁并从内存中加载目标程序集的拷贝。

如果你需要通过上面的方法加载GAC 中的程序集,那么可以通过如下代码实现:

   1: string assemblyName = "AssemblyTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fffb45e56dd478e3";

   2: Assembly ass = Assembly.ReflectionOnlyLoad(assemblyName);

   3: byte[] buffer = System.IO.File.ReadAllBytes(ass.Location);

   4: Assembly assembly = Assembly.Load(buffer);

 

3. Assembly.Load 方法的缓存

在我们使用Assembly.Load 系列方法加载目标程序集时,可能有各种情况导致加载失败,最常见的是目标程序集不存在而导致加载失败问题。失败后我们可能想要再加载一次或者加载多次直到成功为止,但是在.NET Framework 2.0 以后默认是无法实现的,原因在于.NET Framework 2.0 以后 Assembly.Load 方法有缓存,第一次加载目标程序集的失败或者成功的状态都会被缓存,这样在你下一次加载目标程序集时不会真的加载,会直接从缓存里取目标程序集的内容和状态。

对这种情况我们可以在调用Assembly.Load 方法的宿主程序的app.config 中加入如下配置信息来禁用缓存:

<?xml version="1.0"?>

<configuration>

  <runtime>

    <disableCachingBindingFailures enabled="1" />

  </runtime>

  <startup>

    <supportedRuntime version="v2.0.50727"/>

  </startup>

</configuration>

 

4. 还有其他方案吗?

CLR 框架略图:

因为目前Assembly 的加载与卸载是完全由应用程序域控制的,一个程序集只可以通过应用程序域加载,只能通过应用程序域的卸载而卸载,其他任何方式都不可以!!!

那么我们可以在需要时将目标程序集加载进应用程序域,不需要时将应用程序域卸载,但是当前应用程序域的卸载只能通过关闭程序来实现。

到目前为止,看似无解了,但是我们忽略了一个事实,那就是我们可以在当前应用程序域中创建子应用程序域。可以通过在子应用程序域中进行程序集的加载,或者说只要涉及程序集加载的全部放在子应用程序域中,主应用程序域中不做任何与程序集加载有关的事情。

这部分内容我就不详细介绍了,大家可以参考:

http://www.codeproject.com/Articles/42312/Loading-Assemblies-in-Separate-Directories-Into-a

 

5. 我们确实需要使用Assembly.Load吗?

因为Assembly.Load 是将整个程序集以及其相关的依赖程序集全部加载进来,只要有一个出错就会导致加载失败。如果我们只是为了使用当前程序集的类型,而不是使用其方法或者属性的话就完全可以抛弃Assembly.Load 方法。

微软在.Net Framework 2.0 时介绍了几个新的程序集加载APIs:

Assembly.ReflectionOnlyLoadFrom(String assemblyFile)

Assembly.ReflectionOnlyLoad(byte[] rawAssembly)

Assembly.ReflectionOnlyLoad(String assemblyName)

基于开篇提到的Assembly.Load 方法的5种问题, Reflection Only程序集加载APIs 可以:

  1. 跳过程序集强命名认证。
  2. 跳过程序集CAS策略认证。
  3. 跳过处理器架构检查规则。
  4. 不在目标程序中执行任何方法,包括构造函数。
  5. 不应用任何绑定策略。

使用时有如下几个注意事项:

1) CLR 不会搜索目标程序集所依赖的程序集,我们必须通过ReflectionOnlyAssemblyResolve(Assembly.Load 中的对应事件是AssemblyResolve)事件手动处理。

2) 不可以在通过ReflectionOnly 方法加载进来的程序集执行任何方法,包括构造函数,只可以获取程序集的信息和类型。

3) 建议使用Assembly.ReflectionOnlyLoadFrom 方法,但是如果目标程序集在GAC中那么可以使用Assembly.ReflectionOnlyLoad方法。

具体示例代码如下:

using System;

using System.IO;

using System.Reflection;

 

public class ReflectionOnlyLoadTest

{

    private String m_rootAssembly;

    public ReflectionOnlyLoadTest(String rootAssembly)

    {

        m_rootAssembly = rootAssembly;

    }

 

    public static void Main(String[] args)

    {

        if (args.Length != 1)

        {

            Console.WriteLine("Usage: Test assemblyPath");

            return;

        }

 

        try

        {

            ReflectionOnlyLoadTest rolt = new ReflectionOnlyLoadTest(args[0]);

            rolt.Run();

        }

        catch (Exception e)

        {

            Console.WriteLine("Exception: {0}!!!", e.Message);

        }

    }

 

    internal void Run()

    {

        AppDomain curDomain = AppDomain.CurrentDomain;

        curDomain.ReflectionOnlyAssemblyResolve +=

            new ResolveEventHandler(MyReflectionOnlyResolveEventHandler);

        Assembly asm = Assembly.ReflectionOnlyLoadFrom(m_rootAssembly);

        // force loading all the dependencies

        Type[] types = asm.GetTypes();

        // show reflection only assemblies in current appdomain

        Console.WriteLine("------------- Inspection Context --------------");

        foreach (Assembly a in curDomain.ReflectionOnlyGetAssemblies())

        {

            Console.WriteLine("Assembly Location: {0}", a.Location);

            Console.WriteLine("Assembly Name: {0}", a.FullName);

            Console.WriteLine();

        }

    }

 

    private Assembly MyReflectionOnlyResolveEventHandler(object sender, ResolveEventArgs args)

    {

        AssemblyName name = new AssemblyName(args.Name);

        String asmToCheck = Path.GetDirectoryName(m_rootAssembly) + "\\" + name.Name + ".dll";

        if (File.Exists(asmToCheck))

        {

            return Assembly.ReflectionOnlyLoadFrom(asmToCheck);

        }

 

        return Assembly.ReflectionOnlyLoad(args.Name);

    }

}

6. 为什么没有Assembly.UnLoad 方法?

以下是CLR 产品单元经理(Unit Manager) Jason Zander 文章中的内容的整理:

  1) 为了保证 CLR 中代码所引用的代码地址都是有效的,必须跟踪诸如 GC 对象和 COM CCW 之类的特殊应用。否则会出现 Unload 一个 Assembly 后,还有 CLR 对象或 COM 组件使用到这个 Assembly 的代码或数据地址,进而导致访问异常。为了避免这种错误进行的跟踪,目前是在 AppDomain 一级进行的,如果要加入 Assembly.Unload 支持,则跟踪的粒度必须降到 Assembly 一级,这虽然在技术上不是不能实现,但代价太大了。
  2) 如果支持 Assembly.Unload 则必须跟踪每个 Assembly 的代码使用到的句柄和对现有托管代码的引用。例如现在 JITer 在编译方法时,生成代码都在一个统一的区域,如果要支持卸载 Assembly 则必须对每个 Assembly 都进行独立编译。此外还有一些类似的资源使用问题,如果要分离跟踪技术上虽然可行,但代价较大,特别是在诸如 WinCE 这类资源有限的系统上问题比较明显。
  3) CLR 中支持跨 AppDomain 的 Assembly 载入优化,也就是 domain neutral 的优化,使得多个 AppDomain 可以共享一份代码,加快载入速度。而目前 v1.0 和 v1.1 无法处理卸载 domain neutral 类型代码。这也导致实现 Assembly.Unload 完整语义的困难性。

详细请参考: http://www.cnblogs.com/ccBoy/archive/2004/07/13/23636.html

http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx

 

7. 需要牢记的经验

1) 只加载自己需要直接调用的程序集,不加载目标程序集内部引用的程序集和其他无关程序集。

2) 能使用RefelectionLoad 方法加载的程序集绝不要使用Assembly.Load 方法加载。

3) 一旦出现加载错误,不要显而易见认为是程序集不存在!要检查程序集加载缓存、是否出现同一程序集被不同应用程序域加载情况等。

 

至此,我们已经阐述了Assembly.Load 方法的一些特性,你已经了解它了吗?

 

参考链接:

http://msdn.microsoft.com/en-us/library/t07a3dye(v=vs.71).aspx

http://blogs.msdn.com/b/junfeng/archive/2004/11/03/252033.aspx

http://blogs.msdn.com/b/junfeng/archive/2004/08/24/219691.aspx

http://www.sosuo8.com/article/show.asp?id=2979

http://msdn.microsoft.com/en-us/library/ms404279.aspx

http://blog.csdn.net/xt_xiaotian/article/details/6362450

http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx

http://www.cnblogs.com/ccBoy/archive/2004/07/13/23636.html

http://www.cnblogs.com/wayfarer/archive/2004/09/29/47896.html

http://www.codeproject.com/Articles/42312/Loading-Assemblies-in-Separate-Directories-Into-a

http://msdn.microsoft.com/en-us/library/ms404312.aspx

 

 

1.12        引用类型参数一定要使用ref或out

否则对于接口继承者,将可能出现匿名线程方法中对参数的修改而不起作用,如果使用ref或out可屏蔽该问题。

 

1.13        WCF中DataContract和MessageContract的区别

以stream方式上传文件的时候会用到。

如果不使用MessageContract,传入参数或传出参数若有Stream对象,那便只能有一个Stream对象。
比如你传文件,传了文件数据,还想传文件名,就有点愁人了。要用MessageContract,把文件名等等一些文件的元数据用MessageHeader标记,文件数据流用MessageBody标记可以成功。

 

 

 

1.14        未能加载文件或程序集 Microsoft.Office.Interop.Excel 或它的某一个依赖项。系统找不到指定的文件。

 

泪流满面啊,总算解决了,就得不断实践啊……

本机装有Excel 2003、2007、2010,客户机就是装有2003,结果客户机运行程序出现错误。

开始时是在AddReference->Com->添加Excel2003,结果查看属性发现是错误的,实际上真正加载的是Excel2010.查看C:\WINDOWS\assembly发现
   明显被使用的是Excel 2010,卸载又不行,重新注册Excel 2003又不行。 ………………

纠结很久之后,无意间在项目属性中,使用如下的加载项(而不是用DT的COM了)

结果就正常了,哎 F**K MS

 

1.15        Fixed与delegate不能同时使用

http://blog.andreaskahler.com/2009/11/cs-fixed-statement-doesnt-work-with.html

fixed内部使用delegate后,fixed会被微软的.NET编译器优化掉,所以某一时刻fixed的数据会被移动!例如以下代码将不安全.

unsafe byte testB()
{
    var array = new byte[1];
    fixed (byte* p = array)
    {
        Action action = delegate { *p = 22; };
        action();
    }
    return array[0];
}

转载于:https://www.cnblogs.com/BinBin1987/archive/2012/03/10/2389534.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值