从面对《Modern C++ Design》中backEnds.template的用法一无所知开始,要彻底搞清楚C++的模板是如果工作的决心其实已经埋下了。更何况今天在实现B树的时候反复被模板的编译问题折磨着。更加坚定了看完这本《C++ Template. The Complete Guide》的信念。尽管过去对这个主题知道一些东西,什么特化,偏特化,参数演绎等等。但是系统地看一看也是有必要的。当然在这里学习的时候还是非常同意Nocolai的观点,实用才是王道,因为我不是科学家也不可能成为科学家:)。

在这里学习的开始,还是乐意重复一遍那个经典的主题:Why Templates?

理由很简单,在设计数据结构或者算法的时候,需要对不同的数据类型,但是相似的行为做描述。比如说你有一个QuickSort,你想让他能对int,float, std::string都能够排序。

你有很多办法:第一就是你实现3个QuickSort, 参数分别是int[],float[],std::string[],这样是可以工作的。但是带来的问题显而易见,如果你第一次实现的int的排序是错的,那你完蛋了,你必须把这三个实现都改了。如果这个代码是你一个人维护那勉强可以,如果很多人维护,别人可不知道有这样的历史在代码里面。所以这个方案是不妥当的。

第二个方法,其实是比较常见的,就是void*或者Object来取代具体的类型,每次我实现一个容器的时候push_back的内容都是Object&或者void*,用这个Object&或者void*来代替了具体的类型。这样的好处是确实你不用重写三分代码了,但是问题一样很明显,用过了void*以后需要类型转化,原先的类型信息丢失了。在这样的情况下,一个float也被类型转化成了int加入到排序的队列中,而这确实不显示你的想法。如果Object&也一样有问题,如果待排序的数据ObjectA同时继承与ObjectB和ObjectC,而这两个父类又都是Object的子类,那样那个讨厌的菱形有出现了!!!简单的说这种对待排序参数强制的要求在维护性上是得不到很好的保证的。

当然,在模板没有应用的时候,还有人用了宏定义来取代模板。让我们来看看那些曾经熟悉的身影:

#define SWAP(a, b)  {                   \  
                        a ^= b;         \  
                        b ^= a;         \   
                        a ^= b;         \  
                    }   
#define MAX(a, b) ((a) < (b) ? (b) : (a))  

如果不是那些C/C++的大师不断强化这个MAX或者MIN的写法,我想这种"stupid replacement mechanism"肯定会让很多人抓狂。是的,这样的宏定义太容易出错了,在C++里面,他不断的被取代,简单的函数变成了inline,值define编程了enum。总之C++的基调就是忘记宏,忘记预定义。

所以在这样的时刻,在这个C#和Java都在推动template的时刻。我们还有什么理由不用C++的模板呢?

大约是受宏(预处理)的压迫太深,所以有了模板以后的第一件事情便是定义模板函数来取代那些MAX和MIN。作者也乐得如此,下面我就马上来看一个最简单的模板函数max的实现:

template <typename T>  
inline T const& max (T const& a, T const& b)  
{  
    // if a < b then use b else use a  
    return  a < b ? b : a;  
} 

如果费力的逐个解释一下的话,那大概是这样:template表明了这是一个模板函数,<>指定了模板参数区域,typename表明了后面的参数是一个类型名,而T则表明了一种广泛意义上的类型,它可以用来指定所有的类型(int, float, std::string)。所有的这些构成了一个最基本的模板函数,需要说明的一点是由于历史的原因,这里的typename可以用class来取代。(我们确实看到很多都是用class来描述的)。当然也有人问了,那struct是不是也可以,答案当然是不可以!

了解了这个函数的定义,第二步要做的就是探索如何调用它。这里有几个例子:

max(1,2 );      // max<int>  
max('1','2');       // max<char>  
max(1, 2.5 );       // compile error  
max<double>(1,2.5 );  // max<double>  

第一个和第二个调用没有任何问题,就像普通的函数调用,当然他们的调用是可以成功的。但是到第三个调用的时候,问题出现了,编译器报了一个错误'const T &max(const T &,const T &)' : template parameter 'T' is ambiguous。可以看到这里的参数类型出了问题,两个参数的类型不一致。一个是int一个double,所以编译器不知道怎么指定T,注意这里编译器需要的是一个确切的类型,所以他是不会主动给你做任何类型转化的。那怕从int32到int16都不会。所以在这里你需要显示地告诉编译器你要怎么做,这就是第四个调用max<double>(1, 2.5);或者你也可以强制转化一下参数1,让他转化成double然后调用,比如说max(static_cast<double>1, 2.5)

更进一步,我们来看看编译器是如何帮助我们实现这些调用的。简单的说,编译器采用的办法就是存在什么类型的调用,我就给你产生一堆什么类型对应的max代码。比如说max(1,2);调用来的时候,我就给你生成一段这样的代码:

inline int const& max (int const& a, int const& b)   
{  
    return  a < b ? b : a;  
} 
就是用int来替代了原来的T产生了具体类型的代码,这样原来程序员需要为不同类型版本写函数的工作就交给了编译器来做。编译器为不同类型生成对应函数代码的过程被称之为实例化(Instantiation,很巧和OO里面的实例化是同一个词。当然意义是决然不同的)。接下来我们可以看看一个复杂一点的例子:

#include "stdafx.h"  
#include <iostream>  
#include <cstring>  
#include <string>  
// maximum of two values of any type  
template <typename T>  
inline T const& max (T const& a, T const& b)  
{  
    return  a < b  ?  b : a;  
}  
// maximum of two pointers  
template <typename T>  
inline T* const& max (T* const& a, T* const& b)  
{  
    return  *a < *b  ?  b : a;  
}  
// maximum of two C-strings  
inline char const* const& max (char const* const& a,  
                               char const* const& b)  
{   
    return  std::strcmp(a,b) < 0  ?  b : a;  
}  
int _tmain(int argc, _TCHAR* argv[])  
{  
    int a=7;  
    int b=42;  
    ::max(a,b);      // max() for two values of type int  
    std::string s="hey";  
    std::string t="you";  
    ::max(s,t);      // max() for two values of type std::string  
    int* p1 = &b;  
    int* p2 = &a;  
    ::max(p1,p2);    // max() for two pointers  
    char const* s1 = "David";  
    char const* s2 = "Nico";  
    ::max(s1,s2);    // max() for two C-strings  
      
    char* a1 = "CY";  
    char* a2 = "HY";  
    ::max(a1,a2);  
    return 0;  
}  

这是一个完整的运行在VS2005下的例子。这里的不同就是模板函数有了不同的重载的版本,这里在实例化之前,就需要为不同的类型找到一个最合适的函数调用,比如说调用max(a,b);的时候,a,b都是int,编译器发现第一个max函数,也就是以value作为参数的版本最为匹配,所以会就此实例化出一个第一个函数的int版本,注意这里这种匹配的流程被称之为演绎(Deduction)。而演绎的基本的原则就是首先匹配并存的非模板函数,如果不成功就寻找最接近的模板函数。比如max(s1,s2);的时候,匹配到的就是const char*的那个版本的max函数,而max(a1, a2);的时候就是第二个max函数,也就是以指针作为类型参数的版本。

到目前为止还是一派鸟语花香,聪明的编译器做到我们希望的一切,max的表现和预想的出奇的一致。当然不得不提的一点就是平静之下的惊涛暗涌。且看下面的例子:

#include <iostream>  
#include <cstring>  
#include <string>  
// maximum of two values of any type (call-by-reference)  
template <typename T>  
inline T const& max (T const& a, T const& b)  
{  
    return  a < b  ?  b : a;  
}  
// maximum of two C-strings (call-by-value)  
inline char const* max (char const* a, char const* b)  
{   
    return  std::strcmp(a,b) < 0  ?  b : a;  
}  
// maximum of three values of any type (call-by-reference)  
template <typename T>  
inline T const& max (T const& a, T const& b, T const& c)  
{  
    return max (max(a,b), c);  // error, if max(a,b) uses call-by-value  
}  
int main ()  
{  
    ::max(7, 42, 68);     // OK  
    const char* s1 = "frederic";  
    const char* s2 = "anica";  
    const char* s3 = "lucas";  
    ::max(s1, s2, s3);    // ERROR  
}  
可以看到如果这些模板参数类型不匹配的话,带来的问题有多大,可以看到max(7,42,68);的时候没有问题,3个参数的max函数匹配到第一个max函数,这里没有问题,大家都是传引用的语义。但是到max(s1,s2,s3);的时候,那个max(max(a,b),c);匹配到了const char*的max版本,这样,max的返回值就从引用改成了栈上值。尽管这里的comment写了ERROR,但实际上VS2005中只会报一个returning address of local variable or temporary的warning!那些以error为判断条件的筒子们应该注意了,其实这样的warning也会是致命的。所以聪明的编译器带给我们的并不是总是放心的。

在粗粗地说了Function Templates之后,肯定要说说最主要的的Class Templates。因为是开始,所以肯定要说的明白点,清楚点:

#include <vector>  
#include <stdexcept>  
template <typename T>  
class Stack {  
  private:  
    std::vector<T> elems;     // elements  
  public:  
    void push(T const&);      // push element  
    void pop();               // pop element  
    T top() const;            // return top element  
    bool empty() const {      // return whether the stack is empty  
        return elems.empty();  
    }  
};  
template <typename T>  
void Stack<T>::push (T const& elem)  
{  
    elems.push_back(elem);    // append copy of passed elem  
}  
template<typename T>  
void Stack<T>::pop ()  
{  
    if (elems.empty()) {  
        throw std::out_of_range("Stack<>::pop(): empty stack");  
    }  
    elems.pop_back();         // remove last element  
}  
template <typename T>  
T Stack<T>::top () const  
{  
    if (elems.empty()) {  
        throw std::out_of_range("Stack<>::top(): empty stack");  
    }  
    return elems.back();      // return copy of last element  
}  

上面的这个呢就是作者给出的一个最基本的模板类的实现。和模板函数一样,可以看到熟悉的template关键字,<typename T>模板参数的类型标识符。这个实现的好处在于,不仅告诉了你如果申明一个模板类,同时还有如果实现这个模板类的接口。我们可以看到,除了这个Stack<T>::这个类型标识符在这里以外,他和一个模板函数是一样的。。通过这个例子可以发现,如果使用模板类也在其中了,std::vector<T> elems;所以如果我们一旦要使用这个Stack类的话,一样的,也可以用Stack<T> elem;来申明,当然T可以是int, float, std::string...如果是函数调用,那和普通类的调用一模一样。可以说这个实现还是波澜不惊的。

值得注意的一点是我们常常在成熟的类库中看到模板参数也是模板类的定义,例如:typedef Functor<Product*,Seq<int,int> > CreateFunctor;这个东西看似平淡,无非是这个Functor的第二个模板参数是Seq<int,int>而已,但是实际上,这个int>之后必须要有一个空格!没有这个空格,那连续两个大于号>>就被编译器误以为是>>操作符。但是,其实重点说的是但是!!!在VS2005中,编译器已经接受这样的定义,即使没有中间的空格,编译器还是可以正确理解的,只是如果你想写个在各个编译器下都可以跑的代码的话,还是建议你中间加上空格。 (编译器总是在改变,书却没有改变,跟不上时代也是很正常的事情 ^_^)

一般都是这样,在没有模板的时候,我们都期望有这样一个普遍成立的东西可以减少我们的工作,可真有了这个东西以后呢.又发现了这样的代码是针对所有的类型的,如果我需要对某些类型做定制呢.这个也有办法,这个办法就被称之为特化(Specialization).这里有一个Stack类为std::string做特化的例子.

#include "stack1.hpp"  
template<>  
class Stack<std::string> {  
  private:  
    std::deque<std::string> elems;  // elements  
  public:  
    void push(std::string const&);  // push element  
    void pop();                     // pop element  
    std::string top() const;        // return top element  
    bool empty() const {            // return whether the stack is empty  
        return elems.empty();  
    }  
};  
void Stack<std::string>::push (std::string const& elem)  
{  
    elems.push_back(elem);    // append copy of passed elem  
}  
void Stack<std::string>::pop ()  
{  
    if (elems.empty()) {  
        throw std::out_of_range  
                ("Stack<std::string>::pop(): empty stack");  
    }  
    elems.pop_back();         // remove last element  
}  
std::string Stack<std::string>::top () const  
{  
    if (elems.empty()) {  
        throw std::out_of_range  
                ("Stack<std::string>::top(): empty stack");  
    }  
    return elems.back();      // return copy of last element  
}  

在这里用template<>class Stack<std::string> 这样一个头来表示对Stack类进行std::string类型的特化.在原文中有一句话,说的是如果你要特化一个类,那你必须特化所有的函数.当然,必须说明的是,必须特化你会用到的函数,如果这个top的函数以后不会使用.编译器也不会说你特化的不完整.其实如果你需要特化的范围比较小的话,直接特化一个函数也是可以的.比如你可以选择写成这样template<> void Stack<std::string>pop();注意,这里的template<>不写也是可以的.

到现在为止,我们见到的模板类的模板参数都只有一个, 当然我们可以有多个模板参数.这里先来看个刺激的:)

template  
   <  
      class AbstractProduct,  
      typename IdentifierType,  
      typename CreatorParmTList = NullType,  
      template<class> class EncapsulationPolicy = SimplePointer,  
      class CreationPolicy = AlwaysCreate,  
      template <typename , typename> class EvictionPolicy = EvictRandom,  
      class StatisticPolicy = NoStatisticPolicy,  
      template<typename, class> class FactoryErrorPolicy = DefaultFactoryError,  
      class ObjVector = std::vector<AbstractProduct*>  
   >  
class CachedFactory :   
          protected EncapsulationPolicy<AbstractProduct>,  
          public CreationPolicy, public StatisticPolicy, EvictionPolicy< AbstractProduct * , unsigned >  

这个是Loki里面一个寻常的定义,拿出来是想说明类模板是可以有多个模板参数的,同时就像普通的函数定义一样,也可以有默认参数.可能这个例子不是很合适,应该说这里不仅可以有参数,而且可以没有类型的模板参数.就是不是一个typename T类型的参数,比如说可以有个int的参数.像这样的定义也是可以的:template<typename T, int size>class Stack;这个size这里就可以留给 Stack类的使用者来决定(关键是编译器决定)这个Stack的大小. 当然了, int可以那short可以吗, long可以吗?原则上整数类型都是可以的,但是Float和Double直至今日还是不可以的.

回到特化上的事情上来,现在有了这么多参数,你还要特化岂不是很费力?没关系,C++ template还提供一个特性所谓偏特化( Partial Specialization)就是你可以针对一个或者几个模板参数进行特化而不是所有的参数.请原谅我的懒惰这就是一个偏特化的举例:

template<class T, class T2, class T3>class Stack  
{  
public:  
    void f1()  
    {  
    }  
};  
template<class T> class Stack<T, int, long>  
{  
    public:  
    void f1()  
    {  
    }  
};  

当然啦,其实和特化差不多, 只不多是有选择性的而已.

好了,今天也差不多就说这么多吧,现在这个书看起来还是平平淡淡的,还看不出波澜.但是在深入的过程中还是慢慢会展现他的深邃,这个到和<<Modern C++ Design>>完全不同,不过也别有一番风味.

 

转自:http://blog.csdn.net/hhygcy/archive/2009/02/20/3914287.aspx

 

 

 

 

 

 

 

posted @ 2010-11-18 17:34 Eric_Su 阅读(610) 评论(0) 编辑

介绍一种多国语言的实现办法,这也是微软推荐的方式,打开windows mobile下的windows目录可以看到有很多以MUI为后缀名的文件,例如shellres.dll.0804.mui、shell.dll.0804.mui。。。。。。我们可以用eXeScope.exe或者resources hacker这样的文件查看器查看一下这些文件究竟是怎么一回事,不难发现文件里面都是一些资源ID和相对应的字符串。也许你就疑惑这是为什么呢?这些文件有什么作用呢?下面分解。

MUI是Multilingual User Interface的缩写,意为多语言用户界面,主要是用来实现软件的国际化,也就是软件的多国语言版本的实现。其实一个mui文件也是一个DLL来的,但是这个dll有点特殊,不像我们以往的带来了那样包含了很多的函数体供dll外或者本身调用,而是只包含字符串资源,所以可以在vs下新建一个dll项目,不需要其他代码,只需要dll的入口点如下:

#include "windows.h"

 

/////////////////////////////////////////////////////////////////////////////

// DLL Entry Point

 

extern "C"

BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)

{

    return TRUE;

}

除此以外,刚才也提到了,dll里只包含字符串资源,所以必须添加一个资源配置文件如***.rc,在里面添加相应的字符串资源信息,如下:

IDR_HELLO_MENUBAR MENU DISCARDABLE

BEGIN

    MENUITEM "Exit",                          PSP_DLGINDIRECT

    POPUP "Menu"

    BEGIN

        MENUITEM "Exit",                          PSP_DLGINDIRECT

        MENUITEM "Test",                        ID_MENU_TEST

    END

END

在这里要特别注意了,因为每一个mui也就是说每一个dll就代表一种语言,所以在资源配置文件里就是通过对每个字符串资源进行相应语言的更改达到多国语言的效果,例如你要做成简体中文版本,你就必须在重新建一个dll,同样建立一个资源配置文件,但是得把刚才上面的这段资源配置脚本代码改成如下:

IDR_HELLO_MENUBAR MENU DISCARDABLE

BEGIN

    MENUITEM "退出",                          PSP_DLGINDIRECT

    POPUP "菜单"

    BEGIN

        MENUITEM "退出",                          PSP_DLGINDIRECT

        MENUITEM "测试",                        ID_MENU_TEST

    END

END

然后可以通过把定义资源ID的resources.h这个文件共享出来,让多个mui文件和应用程序用同一个资源ID。

同时你还需要在dll项目添加一个导出文件如***.def,其实不需要导出函数名如下就可以:

LIBRARY      "hello.exe.0804.mui"

 

EXPORTS

也许你还会问,那应用程序怎么去通过匹配相应的mui文件来进行语言的定位?请看。

刚才说到windows目录下有shellres.dll.0804.mui,其实mui文件的命名有一定的规则可循,可不是乱来的,首先shellres.dll表明这个mui文件是专门用于shellres.dll多国语言版本,再例如hello.exe.0804.mui这个表示是专用于hello.exe。然后0804这又是表示什么意思呢,这是微软定义的语言ID是一个十六进制数,例如简体中文的ID就是0x0804,而美国英语ID则是0409。其他国家的语言ID可以查看sdk中的Language Identifiers and Locales。

那系统怎么去实现识别应用使用哪一个mui文件呢?在系统注册表中HKEY_CURRENT_USER\Control\Panel\Desktop\MultiUILanguageId记录了系统的默认语言ID,所以系统会根据这个默认ID进行自动配置,而不需程序员再使用代码控制。但是有一点要注意的是mui文件和应用程序要放在同一个目录下。在使用vs生成mui文件时,可以通过对项目属性进行设置,让vs直接生成mui文件,在dll项目中”项目属性”----“链接器”----“常规”中的”输出文件” $(OutDir)/your_mui_name,只需要修改your_mui_name为你相应的mui文件名就可以。

使用mui来实现多国语言版本的实现,不需要更改应用程序的代码,只需要直接添加或删除独立的mui文件,mui文件和应用程序是高分离的,方便管理。

posted @ 2010-11-17 19:34 Eric_Su 阅读(250) 评论(0) 编辑

前言:

在做手机定位应用软件,有两种方法可以实行定位:
1.利用基站定位,通过手机获取基站信息,再根据该信息获取经纬度;
2.利用GPS定位,直接获取经纬度信息。

利弊: 

1.基站定位精确度低,误差范围估计500米以内(这种情况还是在大城市下的,如果是小城市,估计误差更大),但是基站定位可以实现处处定位,即不会因为建筑物等原因而无法获取基站信息。
2.GPS定位精确度高,误差范围估计10米以内,但是需要在空旷的地方,如果周围建筑物多,或者在室内,没法获取GPS信息。
通常用定位这两种方法都采用,也会增加第三种WIFI定位或者小区定位,这是后话。
windows mobile下获得CELLID、LAC的途径有两条:利用串口发送AT指令或是利用RIL来获取。RIL(Radio Interface Layer)是微软自己开发的一个库,它的程序有固有的特点,在获取CELLID上,它其实是对第一种方法的封装,两者本质是一样的。但要注意是:串口一旦打开,就难以关闭,除非重启机器(可能涉及到底层的中断),另外并不是所有的设备都可以取到CELLID。
本文章是利用COM口来获取CELLID,并不保证所有的设备都支持。

测试平台: 

VS2005 + WM 6.0

开发语言: 

C++
定义基站信息结构体:
typedef struct  
{  
 char CountryCode[12];  
 char AreaCode[4];  
 char NetworkCode[4];  
 char CellID[4];  
} TCREG_DATA;
获取基站信息:
void Get_Cellid(void)  
{  
 char m_sTemp[12] = {0};  
 strcat(m_sTemp,"COM");  
 for(int i = 9; i > 0; -- i)  
 {  
  char ch1;  
  _itoa(i,&ch1,10);  
  strcat(m_sTemp,&ch1);  
  strcat(m_sTemp,":");  
  TCREG_DATA* pData = (TCREG_DATA*)GetCREG(m_sTemp);  
  if(!pData)  
   continue;  
  char szNum1[8] = {0};  
  char szNum2[8] = {0};  
  strcpy(szNum1,pData->AreaCode);  
  strcpy(szNum2,pData->CellID);  
  int iLac = (int)strtol(szNum1,NULL,16);  
  int iId  = (int)strtol(szNum2,NULL,16);  
  if (iLac && iId)  
  {  
   sprintf(m_sCell.LAC,"%06d", iLac );  
   sprintf(m_sCell.ID,"%06d", iId  );  
   break;  
  }  
 }  
}  
获取串口:
char* GetCREG( char * comPort )  
{  
 HANDLE hCom;  
 int bufpos;  
 DCB dcb;  
 COMMTIMEOUTS to;  
 DWORD nWritten;  
 DWORD event;  
 DWORD nRead;  
 static char outbuf[20], buf[256];  
 BYTE comdevcmd[2]= {0x84, 0x00};  
 WCHAR m_sCom[12] = {0};  
 mbstowcs(m_sCom,comPort,strlen(comPort));  
 hCom= CreateFile( m_sCom ,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);  
 if (hCom==NULL || hCom==INVALID_HANDLE_VALUE)  
 {  
  hCom= NULL;  
  return NULL;  
 }  
 if (!GetCommState(hCom, &dcb))  
 {  
  return "ERROR:GetCommState Failed";  
 }  
 dcb.BaudRate= CBR_115200;  
 dcb.ByteSize= 8;  
 dcb.fParity= false;  
 dcb.StopBits= ONESTOPBIT;  
 if (!SetCommState(hCom, &dcb))  
 {  
  return "ERROR:SetCommState Failed";  
 }  
 EscapeCommFunction(hCom, SETDTR);  
 EscapeCommFunction(hCom, SETRTS);  
 GetCommTimeouts(hCom, &to);  
 to.ReadIntervalTimeout= 0;  
 to.ReadTotalTimeoutConstant= 200;  
 to.ReadTotalTimeoutMultiplier= 0;  
 to.WriteTotalTimeoutConstant= 20000;  
 to.WriteTotalTimeoutMultiplier= 0;  
 SetCommTimeouts(hCom, &to);  
 if (!SetCommMask(hCom, EV_RXCHAR))  
 {  
  return "-8";  
 }  
 DWORD rildevresult=0,nReturned=0;  
  
 if (!DeviceIoControl (hCom,0xAAAA5679L, comdevcmd, sizeof(comdevcmd),0,0,0,0))  
 {  
  return "-9";  
 }  
 bufpos = 0;  
 strcpy(outbuf,"AT+creg=2\r");  
 if (!WriteFile(hCom, outbuf, 10, &nWritten, NULL))     
 {  
  return "-10";  
 }  
 if (nWritten != 10)  
 {  
  return "-11";  
 }  
 if (!WaitCommEvent(hCom, &event, NULL))  
 {  
  return "-12";  
 }  
 while(1)  
 {  
  if (!ReadFile(hCom, buf+bufpos, 256 - bufpos, &nRead, NULL))  
  {  
   return "-13";  
  }  
  if (nRead == 0)  
   break;  
  bufpos += nRead;  
  
  if (bufpos >= 256)  
   break;  
 }  
 strcpy(outbuf,"AT+creg?\r");  
 if (!WriteFile(hCom, outbuf, 9, &nWritten, NULL))     
 {  
  return "-14";  
 }  
 if (nWritten != 9)  
 {  
  return "-15";  
 }  
 if (!WaitCommEvent(hCom, &event, NULL))  
 {  
  return "-16";  
 }  
 while(1)  
 {  
  if (!ReadFile(hCom, buf+bufpos, 256 - bufpos, &nRead, NULL))  
  {  
   return "-17";  
  }  
  if (nRead == 0)  
   break;  
  bufpos += nRead;  
  if (bufpos >= 256)  
   break;  
 }  
 puts(buf);  
 rildevresult = 0;   
 if (!EscapeCommFunction(hCom, CLRDTR))  
 {  
  return "-4";  
 }  
 if (hCom!=NULL)  
 {  
  CloseHandle(hCom);  
  hCom= NULL;  
 }  
 char* cregResponse = strpbrk( buf, "CREG\0" );  
 return cregResponse;  
}  

总结: 

通过获取基站信息,手机可以实现定位,这对于没有GPS设备的手机来说是一个不错的想法。
posted @ 2010-11-13 13:01 Eric_Su 阅读(870) 评论(0) 编辑

前言:
    在当前手机应用中,可能压缩技术不太引起重视,但是我们可以注意到一点,当手机与服务器进行通信的时候,速度与稳定性是非常重要,在这一环节中,我们如果把原文件(或者原内容)直接发送到服务器,不仅耗费不少流量(在当前流量费不便宜的情况下,这种情况能避免就尽量避免),而且传输的速度慢,消耗的时间长,也有可能产生传输不完整等一系列缺点。所以如果我们采用压缩技术,把要传输到服务器的内容进行压缩,可以减少传输数据量,减少传输时间,提高数据的稳定性,何乐不为!

 

文章介绍:
    采用ZLIB开源进行数据的压缩,比较压缩后内容的大小及传输的时间多少,暂时忽略终端与服务器端压缩与解压缩消耗的时间。

测试平台:
     VS 2005 + WM 6.0 

开发语言:
     C ++

正文:
    1. 下载ZLIB包:http://www.tenik.co.jp/~adachi/wince/zlibce/index.html。选择第三项zlib for WindowsCE Ver.1.1.4(with binaries)。
    2. 下载到本地后解开,在zlibce目录下,有头文件zconf.h和zlib.h;在zlibce\WCE400\ARMV4I下有库文件 zlibce.lib。
    3. 在VS2005下新建一个智能设备项目,设为对话框工程。
    4. 把第2步说到的头文件与库文件复制到工程文件夹中并且在项目中添加以上文件。
    5. 在对话框头文件上添加以下代码,把库文件与头文件载入项目中:
       #include "zlib.h" 
       #pragma comment(lib , "zlibce.lib")
    6. 把一个文本文件,例如test.txt复制到测试手机的My Documents目录下,当前测试的test.txt文件大小为92.8K。
    7. 在OnInitDialog函数中添加以下代码:
       读取文件:

int nLen;  
char *pBuf;  
CFile cf;  
cf.Open(L"\\My Documents\\test.txt", CFile::modeRead);  
nLen = cf.GetLength();  
pBuf = new char[nLen];  
cf.Read(pBuf, nLen); 
压缩:

gzFile zipFile = gzopen("\\My Documents\\test.gz", "wb");  
gzwrite(zipFile, (voidp)pBuf, nLen);  
gzclose(zipFile);

 8. 启动调试

 

总结:
    压缩前文本文件为92.8K,压缩后文件为15.3K,可见压缩比例是接近1:6,通过发送到服务器的时间作一个比较,压缩前发送到服务器消耗时间为33285MS,压缩后发送到服务消耗时间为7214MS,可见发送的时间也大大缩小,有利于我们建立高速稳定的通信环境。

posted @ 2010-11-11 13:01 Eric_Su 阅读(748) 评论(0) 编辑

序言:当我们在手机上开发联网功能时,可以使用手机上设置好的接入点实现手机联网,但是有时候手机的设置不一定会正确,这时如果还是以手机的接入点来联网,程序有出错,我们可以通过程序在手机上创建接入点,然后再通过该接入点进行联网。

创建接入点

void CCommunicate::CreateAccessPoint()   
{   
        CString strFavoriteXml =    
                L"<wap-provisioningdoc>"    
                L"<characteristic type=\"CM_Networks\">"  
                L"<characteristic type=\"Test_GPRS\">"  
                L"<parm name=\"DestId\" value=\"{D1D06580-C364-55ec-9421-6ACF34129C58}\" />"  
                L"</characteristic>"  
                L"</characteristic>"  
                L"<characteristic type=\"CM_GPRSEntries\">"  
                L"<characteristic type=\"Test_GPRS\">"  
                L"<parm name=\"DestId\" value=\"{D1D06580-C364-55ec-9421-6ACF34129C58}\" />"  
                L"<characteristic type=\"DevSpecificCellular\">"  
                L"<parm name=\"BearerInfoValid\" value=\"1\" />"  
                L"<parm name=\"GPRSInfoValid\" value=\"1\" />"  
                L"<parm name=\"GPRSInfoProtocolType\" value=\"2\" />"  
                L"<parm name=\"GPRSInfoL2ProtocolType\" value=\"PPP\" />"  
                L"<parm name=\"GPRSInfoAccessPointName\" value=\"cmwap\" />"  
                L"<parm name=\"GPRSInfoAddress\" value=\"\" />"  
                L"<parm name=\"GPRSInfoDataCompression\" value=\"1\" />"  
                L"<parm name=\"GPRSInfoHeaderCompression\" value=\"1\" />"  
                L"<parm name=\"GPRSInfoParameters\" value=\"\" />"  
                L"</characteristic>"  
                L"</characteristic>"  
                L"</characteristic>"  
                L"<characteristic type=\"CM_ProxyEntries\">"  
                L"<characteristic type=\"WAP\">"  
                L"<parm name=\"SrcId\" value=\"{D1D06580-C364-55ec-9421-6ACF34129C58}\" />"  
                L"<parm name=\"DestId\" value=\"{436EF144-B4FB-4863-A041-8F905A62C572}\" />"  
                L"<parm name=\"Proxy\" value=\"10.0.0.172:80\" />"  
                L"<parm name=\"Enable\" value=\"1\" />"  
                L"<parm name=\"Type\" value=\"1\" />"  
                L"</characteristic>"  
                L"</characteristic>"  
                L"</wap-provisioningdoc>";   
  
        LPWSTR pszwXMLout = NULL;   
        HRESULT hr = DMProcessConfigXML(strFavoriteXml, CFGFLAG_PROCESS, &pszwXMLout);   
  
        if ( pszwXMLout )   
        {   
                delete [] pszwXMLout;   
                pszwXMLout = NULL;   
        }   
} 
使用创建的接入点

int CCommunicate::CreateConnection( HANDLE *phConnection )   
{   
        HRESULT            hResult;   
        //        HANDLE             phConnection;   
        DWORD              dwStatus;    
        DWORD              dwResult;   
        CONNMGR_DESTINATION_INFO cdi;   
        int i;   
        for(i = 0; SUCCEEDED(ConnMgrEnumDestinations(i, &cdi)); i++)   
        {   
                if (0 == wcscmp(cdi.szDescription, _T("Test_GPRS")))   
                        break;   
        }   
        hResult = ConnMgrEnumDestinations(i, &cdi);   
        if (FAILED(hResult))   
        {   
                AfxMessageBox(L"ConnMgrEnumDestinations Error!");   
                return FALSE;   
        }   
        CONNMGR_CONNECTIONINFO ConnInfo;    
        ZeroMemory( &ConnInfo, sizeof( ConnInfo ) );    
        ConnInfo.cbSize = sizeof( ConnInfo );    
        ConnInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;    
        ConnInfo.dwPriority = CONNMGR_PRIORITY_HIPRIBKGND;    
        ConnInfo.dwFlags = CONNMGR_FLAG_PROXY_HTTP | CONNMGR_FLAG_PROXY_WAP;   
        ConnInfo.bExclusive = FALSE;            
        ConnInfo.bDisabled = FALSE;    
        ConnInfo.guidDestNet = cdi.guid;   
        TRACE(_T("ConnectToHttpServer\n") );   
  
        dwStatus = CONNMGR_STATUS_CONNECTED;   
  
        hResult = ConnMgrEstablishConnectionSync( &ConnInfo, phConnection, 75000, &dwStatus );   
        if ( hResult != S_OK )   
        {   
                TRACE(_T("ConnMgrEstablishConnection is error\n") );   
                dwResult = GetLastError();   
                // for debug   
                CString tmp;   
                tmp.Format(_T("%ld"), dwStatus);   
                AfxMessageBox( tmp, MB_OK);   
                return S_FALSE;   
        }   
        else  
        {   
                AfxMessageBox(_T("ConnMgrEstablishEonnection is success\n"), MB_OK);   
                // do something   
        }   
        return S_OK;   
} 
删除创建的接入点

void CCommunicate::ReleaseAccessPoint()   
{   
        CString strFavoriteXml =    
                L"<wap-provisioningdoc>"  
                L"<characteristic type=\"CM_Networks\">"  
                L"<nocharacteristic type=\"Test_GPRS\"/>"  
                L"</characteristic>"    
                L"</wap-provisioningdoc>";   
        LPWSTR pszwXMLout = NULL;   
        HRESULT hr = DMProcessConfigXML(strFavoriteXml, CFGFLAG_PROCESS, &pszwXMLout);   
  
        if ( hr == S_OK)   
        {   
                AfxMessageBox( L"Delete access point success!");   
        }   
        else  
        {   
                AfxMessageBox( L"Delete access point failed!");   
        }   
        if ( pszwXMLout )   
        {   
                delete [] pszwXMLout;   
                pszwXMLout = NULL;   
        }   
}  

posted @ 2010-11-10 15:47 Eric_Su 阅读(378) 评论(0) 编辑
摘要: 对于天气预报的获取,我使用的是Yahoo的API,首先,我们学习一下YAHOO的天气预报API一些使用方法。yahoo天气预报的url是http://weather.yahooapis.com/forecastrss?w=2151330&u=c说说里面参数代表的意思:参数w对应各个地方在yahoo数据库中的WOEID,如北京的WOEID是2151330,目前也可以用参数p来代替w,不过官方...阅读全文
posted @ 2010-11-08 22:47 Eric_Su 阅读(538) 评论(2) 编辑
摘要: 在上一篇文章《Windows Phone 7下ListBox的使用》中,提到了如何在Windows Phone 7下使用ListBox控件,并绑定数据到该控件,以一个售书列表作为例子学习。下面试着更新该例子的UI,使整个界面稍微美观一些(回头看了一下最终效果图,感觉不是那么好看)。在ListBox中嵌入Button,并对Button添加渐变效果。首先,打开上一篇文章的工程BookList,在右边的...阅读全文
posted @ 2010-11-07 01:19 Eric_Su 阅读(555) 评论(2) 编辑
摘要: Windows Phone 7下ListBox的使用学习一下如何在Windows Phone 7下使用ListBox来做一个图书列表。首先打开Microsoft Visual Studio 2010 Express for Windows Phone, 新建一个Silverlightfor phone工程,命名为BookList.右击解决方案的BookList工程,选择Add ->New F...阅读全文
posted @ 2010-11-05 14:31 Eric_Su 阅读(1122) 评论(6) 编辑
摘要: 在上一篇文章中,介绍了如何搭建Windows Phone 7开发环境,现在介绍如何建立一个新的Windows Phone 7工程,这里介绍如何建立一个HELLO WP7工程。首先打开Microsoft Visual Studio 2010 Express for Windows Phone.在File里选择New Project…,新建一个工程,在弹出的窗口中,设置可参考下图所示:点...阅读全文
posted @ 2010-11-04 09:42 Eric_Su 阅读(302) 评论(0) 编辑
摘要: 相对来说,Windows Phone 7的开发环境搭建比较简单,首先,我们来看看Windows Phone 7的系统要求:系统需求:*Windows 7 或者Vista SP2*系统盘上需要3G硬盘空间*2G的内存*Windows Phone Emulator 需要具有WDDM 1.1 驱动的支持 DirectX10 的显卡大致是这样,在做XNA开发的时候,Windows Phone模拟器支持GP...阅读全文
posted @ 2010-11-04 09:37 Eric_Su 阅读(506) 评论(2) 编辑