分类: WINDOWS
2008-06-20 11:43:55
1. appdomain / threadingmodel 方面的一片文章. 最经典的解答就是: 因为非托管代码处于新建立的一个STA thread ,因此它实际上不属于mainthread 所在的已经切换过的appdomain, 因此在这种情况下,new STA object 属于default domain!
2. 我怀疑ATL com 对象在设置为Apartment情况下(Single是正确的),且其设置了类工厂单间模式. 然后这个对象创建了一个dotnetcom对象,然后调用其一个方法,比如Notify, dotnetcom::notify中启动了一个线程,然后设置这个线程为MTA , 假设线程函数叫做threadpoc 在dotnetcom::threadporc 中创建atlcom对象,因为是一个单间所以理论上只是获得了刚才已经创建的对象,然后调用这个atlcom::Method 方法,确发现atlcom::Method 被调用时候的线程ID不是其真正创建时候的threadid,因此怀疑.Net interop 在自动化创建RCW时候对于atlcom对象的调用中少了一个接口列集。
代码如下 文件:wwsdk_predict.rar 大小:72KB 下载:下载
3. 发现.Net COM 和ATL COM交互的时候竟然采用的是一种非常怪异的模式.
1). unmanged exe 访问 managed COM dll , 发现只有一个进程,且如果仅仅是单向访问,那么没有看到ccw 包装的存在,或者这个包装存在于一个系统的dll中. 另外如果这个managed COM dll ref了一个COMdll 那么编译后且运行时加载的interop 就位于 managed COM dll 下.
2) managed exe 访问 unmanaged COM 那么将出现一个managed.vshost.exe 作为载体进程,且这种方式下需要存在interop.dll。
3) managed exe 访问managed COM 然后managed COM 再使用unmanaged COM,我发现C# 中如果 reference 引用了 managed COM 那么就会生成xxx.vshost.exe 以及 clientcomLib.dll , dotnetcom.dll 直接生成.
4) 在3的情况下,如果managed.exe 和managed.dll 同时引用 clientdom.dll 那么实际上只会在manage.exe 目录下产生且运行时候访问的interop.dll for clientdom.dll. 在大多数情况下,标准的RCW或者CCW由runtime生成,为跨越COM和.NET Framework的边界调用提供了足够的列集。使用自定义的属性,可以随意的调整runtime表示托管代码和非托管代码的方式 . 因此我认为我进行的测试发现,C# COM 调用COM DLL 由于我直接进行了引用,因此需要一个给定的interop ,RCW , 实际上这个也是自动生成的,不过不是运行时候生成的,而CCW 在我的测试中发现不需要生成特定的interop CCW.
4. 经过验证interop.dll 如果有多个.Netframework COM组件引用那么,实际上只会加载一个,而且如下.
1) dotnetcom1 ref了 clientcom.dll 那么在它自己的目录下生成一个clientcomLib.dll
2) dotnetcom2 ref了 clientcom.dll 那么它在自己的目录下生成一个clientcomLib.dll
3) client.exe 加载了这两个dotnet COM dll后将以先加载谁作为优先顺序加载这个dll同目录下的dll.
4) 假如client.exe 目录下有clientdllLib.dll 那么加载client.exe 目录下的. 综上所述,实际上sdk仅仅需要提供一份随ww.exe发布的interop.dll即可,当然剩下另外一个问题就是如何确定interop.dll与.Netframework版本的兼容问题.
5. micosfot interop team reply 设置DotNetCom对象的ThreadModel是无效的。因为所有的CCW都会聚合(Aggregate)FTM(Free THreaded Marshaler)。换句话说,经过Marshal的CCW的接口指针仍然是原始指针,而非Proxy。因此,在STA中调用CCW的方法,直接发生在原始指针上,并不会发生Apartment的切换,还是存在于STA之中。对于普通STA/MTA对象来说,跨Apartment拿到的是Proxy,因此会产生Apartment的切换。
6. 今天看到了CLR via c# 中关于强名称,以及GCA , weak name的区别,理解如下
1) strong name是用来解决程序集唯一性的,比如有两个公司开发的相同名称,相同二进制名称的程序集那么按照之前的windows解决方法那么谁后安装谁就是最终执行者. 而在.Net架构下,如果使用了strong name 那么由于程序加入了公私钥判断,因此即使是相同名称在使用了GAC注册到全局库后依然可以进行区分,而如果强行对本地文件进行覆盖,那么之前的应用程序会由于找不到特定strongname的assembly而不会运行.
2) 私有发布和共有发布. 一般来说不建议进行GAC共有发布,一般通过将程序集发布到执行模块同目录下来进行私有发布,私有发布不依赖于注册表,是非常绿的地安装模式。而私有发布是weakname程序集发布的唯一方式,而私有发布意味着运行于其它目录的程序将看不到你的私有发布assembly.
3) .Net 开发的COM和GAC,strongname的关系. .Net COM 开发的dll由于被比如C++ 语言访问的时候需要使用GUID,而按照COM的逻辑GUID需要存储在registry中,因此不管什么.Net发布方式或者strongname 之类的,首先必须 regasm dotnetcom.dll [/codebase] 将这个dotnetcom dll 注册到registry 中. 而实际上在注册表中看到的将是一个 .net engine dll 的入口,看不到dotnetcom.dll 的影子,这样到了C++ 通过CoCreateInstance 通过刚才注册的那个CLSID来创建dotnetcom对象的时候,CLR 寻找真正的dotnetcom.dll 时候那么1) 2) 的规则就起作用了。比如dotnetcom.dll 是私有发布的和application 同在一个目录,那么没有问题,直接找到(比如将插件dll 和aliim.exe 放到同一个目录,当然实际上这个不可能). 比如dotnetcom 采用了strongname 那么可以采用共有发布 gac /i dotnetcom.dll 这样CLR也能够找到这个dll. 最后一种方法就是regasm 后面的[/codebase] 它的含义是在注册表中添加一个codebase选项,当CLR通过私有寻找目录,GAC都无法找到dll的时候将使用这个codebase进行加载. 所以对于我们的aliim plugin system 可以采用codebase 方式,这样就可以解决客户开发的插件使用或者不使用strongname 的问题. 下面这个link中的作者也谈了一个unmanaged code 创建托管COM对象的过程,大致和上面的一致.
http://blog.csdn.net/ATField/archive/2007/08/19/1750890.aspx
下面这篇文章也作了描述,且讲述了如何支持免注册方式. http://blogs.msdn.com/junfeng/archive/2006/05/17/registration-free-com-net-interop.aspx
7. http://blogs.msdn.com/junfeng/archive/2006/05/20/599434.aspx
demo了如何将c++ / C++ CLI / c#的代码编到一起。
8. CLR via c# 中说明app.exe.config 方式实际上可以将程序集部属到其它位置,但是我发现必须将程序集中的所有文件放到这个位置而不能将仅仅某个文件放到指定位置. 象第二章的app.exe ref jefftypes.dll 这个例子,就说明了这个问题,types.dll 实际上引用了两个netmodule ,而app.exe 又引用了jefftypes.dll 因此如果需要将jefftype.dll 为入口清单文件的程序集通过app.exe.config 部属到另外一个目录,这个目录应该是其子目录. 另外在CLR探测程序集时候还有一个例外,就是如果程序集位于入口文件同名的目录下那么就不需要app.exe.config. 如果关心这个详细位置请参考 CLR via C# page 53. 8. 独立创建一个类用于lock的好处. http://blogs.msdn.com/junfeng/archive/2008/04/14/consider-creating-a-new-class-for-locking.aspx
9. 当使用p/invoke方式调用非托管代码时候产生的异常处理。 Reverse P/Invoke and exception http://blogs.msdn.com/junfeng/archive/2008/01/28/reverse-p-invoke-and-exception.aspx
10. CLR 加载assembly时候会作验证,但是有些情况下有些例外?
http://blogs.msdn.com/junfeng/archive/2007/11/12/assembly-loading-and-authorization.aspx
11. windbg 调试CLR时候会根据加载的CLR而加载一个clrdac帮助dll,但是下文可以告诉你可以修改这种行为。 http://blogs.msdn.com/junfeng/archive/2007/08/13/loading-clr-dac-dll-from-a-different-path.aspx
12. CLR load 以及混淆器加载时候需要注意"The bottom line is, CLR assembly loading does not work well against in-place update. Please avoid it if possible. " http://blogs.msdn.com/junfeng/archive/2007/08/06/the-unusual-ref-def-mismatch-fileloadexception.aspx
13. p/invoke 在marshalling时候效率的考虑 http://blogs.msdn.com/junfeng/archive/2007/07/09/reverse-p-invoke-marshaling-performance.aspx
14. genman32 一个可以为.net组件产生sxs免注册信息的工具 http://blogs.msdn.com/junfeng/archive/2007/04/14/genman32-a-tool-to-generate-sxs-manifest-for-managed-assembly-for-registration-free-com-net-interop.aspx
15. .Net 线程以及线程池 http://www.cnblogs.com/nokiaguy/archive/2008/07/16/1244746.html
16. 工作流 http://www.cnblogs.com/wfplatform/archive/2008/07/25/1251608.html
17. 图解dotnet framework http://www.cnblogs.com/xugao918/category/145674.html
18. donet essential 的例子中显示将文件放到一个目录下能够自动引用到,只要设定public。比如一个文件code.cs 中定义了一个类CCode , 另外一个文件client.cs 就可以直接引用到。 下面的代码就展示了3种引用模式,其中第一种是隐式,后面两种是显式,其中前一种根据绝对文件名称,后者则用到dotnet的默认模块搜索规则
// implict way component comp = new component();
comp.func();
// explict way 1
Assembly comp = Assembly.LoadFrom("./component.dll");
// explict way 2
Assembly comp = Assembly.Load("component,version=1.0.0.0,culture=neutral,publickeytoken=null");
Object obj = comp.CreateInstance("myapp.component");
component com = obj as component;
if (com != null)
{
com.func();
}
19. /platform 参数对于C#编译后的版本运行影响。 anycpu , 那么将由CLR自行决定当前的运行平台。换句话说,32/64/ia64架构只要有CLR那么都将以特定cpu架构方式运行。 x86 , 在32位机器上正常运行;64位机器上wow64模拟运行,ia64 wow64模拟运行。
20. 编译器开关对优化的影响. /optimize- /debug- (default) IL(未优化) JIT(Native)优化 /optimize- /debug(+/full/pdbonly) IL(未优化) JIT(Native)未优化 /optimize+ /debug(any) IL(优化) JIT(Native)优化这儿有个例外,只要将调试器attach到一个含有CLR的进程,那么JIT编译器始终生成未优化的代码帮助调试。
21. 目前IL语言的版权保护,貌似只能通过将核心算法放到非托管模块中。
22. CLS语言相容性开关。 [assembly: CLSCompliant(true)] 将打开.
23. 程序集文件在编译时刻需要全部存在,但是在运行时候不不需要如此。
24. 程序集load时候的版本选择。 程序总是加载和当前CLR版本匹配的程序集,也就是说即使程序编译后生成的清单文件说mscorelib.dll 需要的版本是2.x,但是如果在目标及其上运行时候CLR是3。x那么还是会加载3.x的dll.
25. .Net 加载程序集时候,如果是指定了部分路径名,那么将按照 1) 应用程序当前路径 2) csc.exe 的路径,CLR dll 所在路径 3) /lib LIB 指定路径.
26. .Net 程序在帮定到程序集时候,可以理解为一种隐式加载,顺序, (强名称)
1) 根据 name+version+culture+pubkeytoken 方式在GAC查找
2) app 目录
3) app.config
27. CLR 如何引用一个类型?
28. .net 类型的区分 分为值类型,和引用类型。前者相当于可以理解为简单类型,这种对象是通过stack创建的; 而后者对象类型不管是通过new创建的还是不是,都创建在heap上, 堆上对象都有一些额外成员,且这些成员必须被初始化, 对象的其他字段总设为0, 从托管堆分配一个对象,可能导致一次垃圾收集 值类型的选择
1) 类型具有一个基元行为,这是一个简单类型,其中没有任何成员会修改实例字段。
2) 类型不需要从其它类型继承
3) 类型不会派生出其它任何类型 将值类型复制的时候,将执行按值copy,逐一复制字段 将引用类型复制的时候,仅仅复制内存地址。 值类型在内存被回收之后,它不会通过finalize 收到通知。
29. 类型checked,unchecked操作。 一般来说,操作符号导致的值溢出,那么如果使用 unchecked 那么就不会抛出异常,反之则抛出异常。
30. 值类型,引用类型都可以通过 [StructLayout(LayoutKind.Auto)] 这种方式控制是否调整结构的字段排序,一般建议结构体保持用户定义的顺序,而引用类型则自动排列。