Modest opinions  
by a humble autodidact
公告
  • 昵称:yushih
    园龄:3年11个月
    粉丝:2
    关注:2
日历
<2008年3月>
2425262728291
2345678
9101112131415
16171819202122
23242526272829
303112345
统计
  • 随笔 - 57
  • 文章 - 1
  • 评论 - 133
  • 引用 - 1

导航

搜索

 
 

常用链接

我的标签

随笔档案

最新评论

阅读排行榜

评论排行榜

推荐排行榜

 

2008年3月6日

虽然在C++/CLI中调用DLL里的native函数时,P/Invoke可以自动把System::String列集成C字串(const char*或const wchar_t*),但有的时候我们仍然需要手工列集System::String。比如当出现以下情况时:1.托管代码要调用同属一个mixed assembly里的非托管函数(比如和一个非托管静态库链接),并传递一个字串,此时P/Invoke自动列集不能发挥作用,因为这项功能只针对从DLL里导入的函数。2.调用一个以非托管指针操作C字串的托管函数,如编译成IL代码的老的C/C++函数。为了简便手工列集System::String,我写了几个辅助类。以下例子说明如何使用:
#include <iostream>

#include "StringMarshaller.h"


int main()
{
using namespace std;
using namespace System;
 
 String
^ s1 = "sometimes ";
 String
^ s2 = "C++/CLI ";
 String
^ s3 = "sucks";

 wcout 
<< (const wchar_t*)StringMarshaller::MarshalStringUni(s1);
 cout 
<< StringMarshaller::MarshalStringAnsi(s2);
 wcout 
<< (const wchar_t*)StringMarshaller::MarshalStringPin(s3) << endl;
}
可以看到在StringMarshaller.h的名字空间StringMarshaller里有三个“函数”MarshalStringUni, MarshalStringAnsi和MarshalStringPin。其中前两个要将传递的内容进行一次拷贝,最后一个不拷贝,但是会在被调用函数返回之前pin住传递的System::String。MarshalStringPin得到的是unicode字串,因为CLR中的字串是以unicode方式储存的。需要注意的是使用这三个“函数”的条件是被调用函数不会保存传入的const char或const wchar_t指针,因为被调用函数返回后这些指针就失效了。
以下是StringMarshaller.h的源码:
#pragma once
#include <vcclr.h>

namespace StringMarshaller {
using namespace System;
using namespace System::Runtime::InteropServices;
/////////////////////////////////////////////////////////////////////
  // The copying version
template<typename CharT> // CharT = char or unsigned char or wchar_t
class copy_marshaller
{
 IntPtr m;

public:
 copy_marshaller(IntPtr x) 
{ m=x; }
 
 
operator const CharT*() 
 
return static_cast<const CharT*>(m.ToPointer()); }
 
 
~copy_marshaller() 
 
{ Marshal::FreeHGlobal(m); }
}
;

#define DEFINE_MarshalString(encoding, type)\
struct MarshalString##encoding: public copy_marshaller<type>\
{\
 MarshalString##encoding(String
^ s)\
 : copy_marshaller
<type>(Marshal::StringToHGlobal##encoding(s))\
 
{}\
}
;

DEFINE_MarshalString(Ansi, 
char) //
MarshalStringAnsi
DEFINE_MarshalString(Uni, wchar_t) // MarshalStringUni

/////////////////////////////////////////////////////////////////////
  // The pining version.
class MarshalStringPin
{
 GCHandle h;

public:
 MarshalStringPin(String^ s)
 {
  h = GCHandle::Alloc(s, GCHandleType::Pinned);
 }

 operator const wchar_t*()
 {
   return reinterpret_cast<wchar_t*>(
    h.AddrOfPinnedObject().ToPointer());
 }

 ~MarshalStringPin() 
 {
  h.Free();
 }
};

}





posted @ 2008-03-06 23:50 yushih 阅读(218) 评论(0) 编辑
 
个人认为,C++/CLI的WinForm designer是个败笔,败就败在它非要和相应的C++/CLI代码实时对应。这样的结果就是用起来很迟钝,非常的不流畅,非常的不爽,估计就是前列腺炎的虚拟体验。增加、删除、修改control和切换窗口的时候都有明显的延迟,而且伴随硬盘动作。须知我打Day of Defeat的时候,延迟大于80ms的服务器是不会进的。我认为不是我机器配置不够,我的CPU是3800+X2,硬盘有?m的缓存,内存不多,1.5G,但是1.Visual Studio,包括Windows的cache manager,并没用把这些内存用完,2.本人的机子上没有病毒(supposedly)和防毒软件(两者并列世界上最邪恶的程序),3.本人的操作系统是小巧灵动节约资源的Windows XP(真是没想到XP也有今天,但是不出几年Vista也能得到这样的评价)。为什么怀疑这样的延迟是“和相应的C++/CLI代码实时对应”引起的呢?因为Visual Studio 6的dialog editor没有这样的功能,也没有这样的毛病,即使在性能差得多的机子上也没有可感知的延迟。忍受了如此缺陷,这个功能带来了什么好处呢?啥也没有。WinForm designer生成的代码并不好看:随着用户在WinForm designer里反复添加删除controls及其属性,生成的代码到处是乱糟糟的缩进,随机出现的空白行、accessibility modifiers。把和WinForm对应的C++/CLI代码暴露出来,是方便程序员用代码控制UI吗?比如你想把某控件的宽度设置成分辨率的函数,而不是一个常数?也不是。WinForm designer生成的代码根本不能手工修改,除非你准备从此以后再不用WinForm designer。如果你过度的修改了WinForm designer生成的代码,既便得到的代码可以编译、运行,WinForm designer也可能无法解析,而不能映射成图形,只在屏幕上留下几个exception。
基于以上的原因,当我不得不用WinForm时,我根本不以代码窗口打开WinForm designer使用的那个头文件。我会另建一个头文件,定义一个class,继承WinForm designer生成的class,然后在这个新的class搞进一步初始化,event handling之类的事。或者让Form中的controls都成为public成员,在另外的文件里用一个controller类包含这个Form类,访问相关controls,比如安装event handlers。
我希望WinForm designer在内存中用二进制的对象结构,比如一个syntax tree,记录正在被修改的Form,并将相关信息(Form衍生类的public部分)告知IntelliSense。编译的时候直接用这个二进制的对象结构生成IL代码就可以了。我不需要看到我正在编辑的WinForm的C++/CLI代码。
不知道用WinForm designer生成C#代码有没有这样的问题?有体验的读者请留下评论。谢谢。
posted @ 2008-03-06 00:44 yushih 阅读(403) 评论(2) 编辑
 
Copyright © yushih Powered by: 博客园 模板提供:沪江博客