精华内容
下载资源
问答
  • 目前互联网上的蓝牙相互通信...当两台windows系统电脑蓝牙配对完成并安装上此程序后,便可实现蓝牙之间互发字符,字符串,文件进行实时交互通信。 采用此软件当A电脑主动连接B电脑蓝牙时,B电脑会监听到此动作并去连...

    目前互联网上的蓝牙相互通信的资源少之又少,应广大网友要求,特开发出此软件,用于电脑蓝牙之间互发字符,字符串,文件的实时交互通信。其采用c#语言,利用inTheHand.Net.Personal库实现。从而弥补c#在这一领域上的空白。

    一、软件功能

    • 当两台windows系统电脑蓝牙配对完成并安装上此程序后,便可实现蓝牙之间互发字符,字符串,文件进行实时交互通信。
    • 采用此软件当A电脑主动连接B电脑蓝牙时,B电脑会监听到此动作并去连接A电脑作为回应,此时便可进行实时通信。
    • 在实时通信时,两个软件可以互发互收文件及字符或字符串消息(经过测试,文件和字符串大小及种类没有影响)而互不干扰。
    • 实时通信时,两个软件每接收或发送消息(字符或字符串)时,其内容都会记录在会话列表中。
    • 通过以上描述,你可以理解为其就是一个简易版QQ。
    • 其还增加了监听模式,当A关闭监听时,B蓝牙不可直接连上A蓝牙。
    • 当两软件相互连接时,若A电脑关闭监听,A电脑将不会实时收到消息及文件,仅当再开启监听时,消息和文件才会陆续接收,此功能如QQ的离线留言
    • 当连接的一方主动断开连接或退出软件时,另一方会监听到此消息,并放弃连接作为回应。
    • 此软件可以作为电脑与其他设备(嵌入式,手机等蓝牙设备)交互的windows客户端,可以与其进行交互。
    • 软件可以系统托盘方式运行。
    • 更改与修复了大量bug,更加稳定。

    二、软件截图

    在这里插入图片描述

    三、软件打包下载

    百度网盘下载

    四、软件再开发

    在此软件的基础上还可以进行二次开发

    • 搜索附近的蓝牙设备并进行配对请求,相当于附近的人,并添加其为好友
    • 关闭设备可见,以便附近设备无法搜索到,相当于隐身模式
    • 配对的设备放在好友列表里,并把打开蓝牙的设备显示为上线,即可选择好友发送。可以实现QQ的一些基本功能。
      以上设想仅供有志之士参考,恕个人能力有限,未能实现。
      四、软件源代码下载
      如需帮助,请从以下链接里找到我的联系方式。
      蓝牙实时通信c#实现完整项目文件源代码下载
    展开全文
  • Delphi技巧-字符串

    千次阅读 2006-04-10 19:36:00
    Pascal 传统的字符串操作方法与Windows 不同,Windows吸取了C语言的字符串操作方法。32位Delphi中增加了长字符串类型,该类型功能强大,是Delphi 确省的字符串类型。字符串类型 在Borland公司的Turbo Pascal和16位...
     Delphi 中字符串的操作很简单,但幕后情况却相当复杂。Pascal 传统的字符串操作方法与Windows 不同,Windows吸取了C语言的字符串操作方法。32位Delphi中增加了长字符串类型,该类型功能强大,是Delphi 确省的字符串类型。 
    

    字符串类型
    在Borland公司的Turbo Pascal和16位Delphi中,传统的字符串类型是一个字符序列,序列的头部是一个长度字节,指示当前字符串的长度。由于只用一个字节来表示字符串的长度,所以字符串不能超过255个字符。这一长度限制为字符串操作带来不便,因为每个字符串必须定长(确省最大值为255),当然你也可以声明更短的字符串以节约存储空间。 字符串类型与数组类型相似。实际上一个字符串差不多就是一个字符类型的数组,因为用[]符号,你就能访问字符串中的字符,这一事实充分说明了上述观点。

    为克服传统Pascal 字符串的局限性,32位Delphi增加了对长字符串的支持。这样共有三种字符串类型:

    ShortString 短字符串类型也就是前面所述的传统 Pascal 字符串类型。这类字符串最多只能有255个字符,与16位Delphi中的字符串相同。短字符串中的每个字符都属于ANSIChar 类型(标准字符类型)。
    ANSIString长字符串类型就是新增的可变长字符串类型。这类字符串的内存动态分配,引用计数,并使用了更新前拷贝(copy--on-write)技术。这类字符串长度没有限制(可以存储多达20亿个字符!),其字符类型也是ANSIChar 类型。
    WideString 长字符串类型与ANSIString 类型相似,只是它基于WideChar 字符类型,WideChar 字符为双字节Unicode 字符。
    使用长字符串
    如果只简单地用String定义字符串,那么该字符串可能是短字符串也可能是ANSI长字符串,这取决于$H 编译指令的值,$H+(确省)代表长字符串(ANSIString 类型)。长字符串是Delphi 库中控件使用的字符串。

    Delphi 长字符串基于引用计数机制,通过引用计数追踪内存中引用同一字符串的字符串变量,当字符串不再使用时,也就是说引用计数为零时,释放内存。

    如果你要增加字符串的长度,而该字符串邻近又没有空闲的内存,即在同一存储单元字符串已没有扩展的余地,这时字符串必须被完整地拷贝到另一个存储单元。当这种情况发生时,Delphi运行时间支持程序会以完全透明的方式为字符串重新分配内存。为了有效地分配所需的存储空间,你可以用SetLength 过程设定字符串的最大长度值:

    SetLength (String1, 200);
    SetLength 过程只是完成一个内存请求,并没有实际分配内存。它只是把将来所需的内存预留出来,实际上并没有使用这段内存。这一技术源于Windows 操作系统,现被Delphi用来动态分配内存。例如,当你请求一个很大的数组时,系统会将数组内存预留出来,但并没有把内存分配给数组。

    一般不需要设置字符串的长度,不过当需要把长字符串作为参数传递给API 函数时(经过类型转换后),你必须用SetLength 为该字符串预留内存空间,这一点我会在后面进行说明。

    看一看内存中的字符串
    为了帮你更好地理解字符串的内存管理细节,我写了一个简例StrRef 。在程序中我声明了两个全程字符串:Str1 和 Str2,当按下第一个按钮时,程序把一个字符串常量赋给第一个变量,然后把第一个变量赋给第二个: Str1 := 'Hello';
    Str2 := Str1;
    除了字符串操作外,程序还用下面的StringStatus 函数在一个列表框中显示字符串的内部状态:

    function StringStatus (const Str: string): string;
    begin
    Result := 'Address: ' + IntToStr (Integer (Str)) +
    ', Length: ' + IntToStr (Length (Str)) +
    ', References: ' + IntToStr (PInteger (Integer (Str) - 8)^) +
    ', Value: ' + Str;
    end;
    在StringStatus 函数中,用常量参数传递字符串至关重要。用拷贝方式(值参)传递会引起副作用,因为函数执行过程中会产生一个对字符串的额外引用;与此相反,通过引用(var)或常量(const)参数传递不会产生这种情况。由于本例不希望字符串被修改,因此选用常量参数。

    为获取字符串内存地址(有利于识别串的实际内容也有助于观察两个不同的串变量是否引用了同一内存区),我通过类型映射把字符串类型强行转换为整型。字符串实际上是引用,也就是指针:字符串变量保存的是字符串的实际内存地址。

    为了提取引用计数信息,我利用了一个鲜为人知的事实:即字符串长度和引用计数信息实际上保存在字符串中, 位于实际内容和字符串变量所指的内存位置之前,其负偏移量对字符串长度来说是-4(用Length 函数很容易得到这个值),对引用记数来说是-8。

    不过必须记住,以上关于偏移量的内部信息在未来的Delphi版本中可能会变,没有写入正式Delphi文档的特性很难保证将来不变。

    通过运行这个例子,你会看到两个串内容相同、内存位置相同、引用记数为2,如图7.1中列表框上部所示。现在,如果你改变其中一个字符串的值,那么更新后字符串的内存地址将会改变。这是copy-on-write技术的结果。

    图 7.1: 例StrRef显示两个串的内部状态,包括当前引用计数

     

    第二个按钮(Change)的OnClick 事件代码如下,结果如图7.1列表框第二部分所示:

    procedure TFormStrRef.BtnChangeClick(Sender: TObject);
    begin
    Str1 [2] := 'a';
    ListBox1.Items.Add ('Str1 [2] := ''a''');
    ListBox1.Items.Add ('Str1 - ' + StringStatus (Str1));
    ListBox1.Items.Add ('Str2 - ' + StringStatus (Str2));
    end;
    注意,BtnChangeClick 只能在执行完BtnAssignClick 后才能执行。为此,程序启动后第二个按钮不能用(按钮的Enabled 属性设成False);第一个方法结束后激活第二个按钮。你可以自由地扩展这个例子,用StringStatus 函数探究其它情况下长字符串的特性。

    Delphi 字符串与 Windows PChar字符串
    长字符串为零终止串,这意味着长字符串完全与Windows使用的C语言零终止串兼容,这给长字符串使用带来了便利。一个零终止串是一个字符序列,该序列以一个零字节(或null)结尾。零终止串在Delphi中可用下标从零开始的字符数组表示,C语言就是用这种数组类型定义字符串,因此零终止字符数组在Windows API 函数(基于C语言)中很常见。由于Pascal长字符串与C语言的零终止字符串完全兼容,因此当需要把字符串传递给Windows API 函数时,你可以直接把长字符串映射为PChar 类型。

    下例把一个窗体的标题拷贝给PChar 字符串(用API 函数GetWindowText),然后再把它拷贝给按钮的Caption 属性,代码如下:

    procedure TForm1.Button1Click (Sender: TObject);
    var
    S1: String;
    begin
    SetLength (S1, 100);
    GetWindowText (Handle, PChar (S1), Length (S1));
    Button1.Caption := S1;
    end;
    你可以在例LongStr 中找到这段代码。注意:代码中用SetLength函数为字符串分配内存,假如内存分配失败,那么程序就会崩溃;如果你直接用PChar 类型传递值(而不是象以以上代码那样接受一个值),那么代码会很简单,因为不需要定义临时字符串,也不需要初始化串。下面代码把一个Label(标签)控件的Caption 属性作为参数传递给了API函数,只需要简单地把属性值映射为PChar类型:

    SetWindowText (Handle, PChar (Label1.Caption));
    当需要把WideString 映射为Windows兼容类型时,你必须用PWideChar 代替PChar进行转换,WideString常用于OLE和 COM 程序。

    刚才展现了长字符串的优点,现在谈谈它的弊端。当你把长字符串转换为PChar 类型时可能会引发一些问题,问题根本在于:转换以后字符串及其内容将由你来负责,Delphi 不再管了。现在把上面Button1Click代码稍作修改:

    procedure TForm1.Button2Click(Sender: TObject);
    var
    S1: String;
    begin
    SetLength (S1, 100);
    GetWindowText (Handle, PChar (S1), Length (S1));
    S1 := S1 + ' is the title'; // this won't work
    Button1.Caption := S1;
    end;
    程序编译通过,但执行结果会令你惊讶,因为按钮的标题并没变,所加的常量字符串没有添加到按钮标题中。问题原因是Windows写字符串时(在GetWindowText API调用中),Windows 没有正确设置Pascal 长字符串的长度。Delphi 仍可以输出该字符串,并能通过零终止符判断字符串何时结束,但是如果你在零终止符后添加更多的字符,那么这些字符将被忽略。

    怎么解决这个问题呢?解决方法是告诉系统把GetWindowText API函数返回的字符串再转换成Pascal字符串。然而,如果你用以下代码:

    S1 := String (S1);
    Delphi 系统将不予理睬,因为把一种类型转换为它自己的类型是无用的操作。为获得正确的Pascal 长字符串,需要你把字符串重新映射为一个PChar 字符串,然后让Delphi 再把它转回到字符串:

    S1 := String (PChar (S1));
    实际上,你可以跳过字符串转换(S1 := PChar (S1));, 因为在Delphi中Pchar转换到string是自动执行的,最终代码如下:

    procedure TForm1.Button3Click(Sender: TObject);
    var
    S1: String;
    begin
    SetLength (S1, 100);
    GetWindowText (Handle, PChar (S1), Length (S1));
    S1 := String (PChar (S1));
    S1 := S1 + ' is the title';
    Button3.Caption := S1;
    end;
    另一个办法是用PChar 字符串的长度重新设定Delphi 字符串长度,可以这样写:

    SetLength (S1, StrLen (PChar (S1)));
    在例LongStr中你可以看到三种方法的结果,分别由三个按钮执行。如果只想访问窗体标题,仅需要用到窗体对象本身的Caption 属性,没有必要写这段迷糊人的代码,这段代码只是用来说明字符串转换问题。当调用Windows API 函数时会遇到这种实际问题,那时你就不得不考虑这一复杂情况了。

    格式化字符串
    使用加号(+)操作符和转换函数(如IntToStr),你确实能把已有值组合成字符串,不过另有一种方法能格式化数字、货币值和其他字符串,这就是功能强大的Format 函数及其一族。

    Format 函数参数包括:一个基本文本字符串、一些占位符(通常由%符号标出)和一个数值数组,数组中每个值对应一个占位符。例如,把两个数字格式化为字符串的代码如下:

    Format ('First %d, Second %d', [n1, n2]);
    其中n1和n2是两个整数值,第一个占位符由第一个值替代,第二个占位符由第二个值替代,以此类推。如果占位符输出类型(由%符号后面的字母表示)与对应的参数类型不匹配,将产生一个运行时间错误,因此设置编译时间类型检查会有利于Format 函数的使用。

    除了%d外,Format 函数还定义了许多占位符,见表7.1。这些占位符定义了相应数据类型的默认输出,你可以用更深一层的格式化约束改变默认输出,例如一个宽度约束决定了输出中的字符个数,而精度约束决定了小数点的位数。例如

    Format ('%8d', [n1]);
    该句把数字n1转换成有8个字符的字符串,并通过填充空白使文本右对齐,左对齐用减号(-) 。

    表 7.1: Format函数的占位符

    占位符 说明
    d (decimal) 将整型值转换为十进制数字字符串
    x (hexadecimal) 将整型值转换为十六进制数字字符串
    p (pointer) 将指针值转换为十六进制数字字符串
    s (string) 拷贝字符串、字符、或字符指针值到一个输出字符串
    e (exponential) 将浮点值转换为指数表示的字符串
    f (floating point) 将浮点值转换为浮点表示的字符串
    g (general) 使用浮点或指数将浮点值转换为最短的十进制字符串
    n (number) 将浮点值转换为带千位分隔符的浮点值
    m (money) 将浮点值转换为现金数量表示的字符串,转换结果取决于地域设置,详见Delphi帮助文件的Currency and date/time formatting variables主题


    领会以上内容最好的办法是你亲自进行字符串格式化试验。为了简便起见,我写了FmtTest 程序,它能将整数和浮点数转换为格式化字符串。从图7.2可见,程序窗体分为左右两部分,左边对应整型数字转换,右边对应浮点数转换。

    各部分的第一个编辑框显示需要格式化为字符串的数值。第一个编辑框下方有一个按钮,用来执行格式化操作并在消息框中显示结果;紧接着第二个编辑框用于输入格式化类型串。你也可以单击ListBox 控件中的任一行,选择预定义的格式化类型串,也可以自行输入,每输入一个新的格式化类型串,该类型串就会被添加到列表框中(注意,关闭程序就失去了添加的类型)。 图 7.2: 程序 FmtTest 的浮点值输出

     

    本例只简单使用了不同的控制文本来产生输出,下面列出了其中一个Show 按钮事件代码:

    procedure TFormFmtTest.BtnIntClick(Sender: TObject);
    begin
    ShowMessage (Format (EditFmtInt.Text,
    [StrToInt (EditInt.Text)]));
    // if the item is not there, add it
    if ListBoxInt.Items.IndexOf (EditFmtInt.Text) < 0 then
    ListBoxInt.Items.Add (EditFmtInt.Text);
    end;
    这段代码主要用EditFmtInt 编辑框的文本和EditInt 控件的值进行了格式化操作。如果格式化类型串没有在列表框中列出,那么输入的串会被添加到列表框中;如果用户在列表框中进行点击,代码会把点击的串移到编辑框中:

    procedure TFormFmtTest.ListBoxIntClick(Sender: TObject);
    begin
    EditFmtInt.Text := ListBoxInt.Items [
    ListBoxInt.ItemIndex];
    end

    展开全文
  • C++字符串完全指南

    千次阅读 2010-02-01 10:37:00
    http://langzi0115.bokee.com/3879634.htmlC++字符串完全指南 - Win32字符...这个指南的目的就是说明各种字符串类型及其用途,并说明如何在必要时进行类型的相互转换。在指南的第一部分,介绍三种字符编码格式。理解编

    http://langzi0115.bokee.com/3879634.html

    C++字符串完全指南 - Win32字符编码(一)

    前言

    字符串的表现形式各异,象TCHAR,std::string,BSTR等等,有时还会见到怪怪的用_tcs起头的宏。这个指南的目的就是说明各种字符串类型及其用途,并说明如何在必要时进行类型的相互转换。

    在指南的第一部分,介绍三种字符编码格式。理解编码的工作原理是致为重要的。即使你已经知道字符串是一个字符的数组这样的概念,也请阅读本文,它会让你明白各种字符串类之间的关系。

    指南的第二部分,将阐述各个字符串类,什么时候使用哪种字符串类,及其相互转换。

    字符串基础 - ASCII, DBCS, Unicode

    所有的字符串类都起源于C语言的字符串,而C语言字符串则是字符的数组。首先了解一下字符类型。有三种编码方式和三种字符类型。

    第一种编码方式是单字节字符集,称之为SBCS,它的所有字符都只有一个字节的长度。ASCII码就是SBCS。SBCS字符串由一个零字节结尾。

    第二种编码方式是多字节字符集,称之为MBCS,它包含的字符中有单字节长的字符,也有多字节长的字符。Windows用到的MBCS只有二种字符类型,单字节字符和双字节字符。因此Windows中用得最多的字符是双字节字符集,即DBCS,通常用它来代替MBCS。

    在 DBCS编码中,用一些保留值来指明该字符属于双字节字符。例如,Shift-JIS(通用日语)编码中,值0x81-0x9F 和 0xE0-0xFC 的意思是:“这是一个双字节字符,下一个字节是这个字符的一部分”。这样的值通常称为前导字节(lead byte),总是大于0x7F。前导字节后面是跟随字节(trail byte)。DBCS的跟随字节可以是任何非零值。与SBCS一样,DBCS字符串也由一个零字节结尾。

    第三种编码方式是Unicode。 Unicode编码标准中的所有字符都是双字节长。有时也将Unicode称为宽字符集(wide characters),因为它的字符比单字节字符更宽(使用更多内存)。注意,Unicode不是MBCS - 区别在于MBCS编码中的字符长度是不同的。Unicode字符串用二个零字节字符结尾(一个宽字符的零值编码)。

    单字节字符集是拉丁字母,重音文字,用ASCII标准定义,用于DOS操作系统。双字节字符集用于东亚和中东语言。Unicode用于COM和Windows NT内部。

    读者都很熟悉单字节字符集,它的数据类型是char。双字节字符集也使用char数据类型(双字节字符集中的许多古怪处之一)。Unicode字符集用wchar_t数据类型。Unicode字符串用L前缀起头,如:

    wchar_t wch = L'1'; // 2 个字节, 0x0031

    wchar_t* wsz = L"Hello"; // 12 个字节, 6 个宽字符

    字符串的存储

    单字节字符串顺序存放各个字符,并用零字节表示字符串结尾。例如,字符串"Bob"的存储格式为:

    Unicode编码中,L"Bob"的存储格式为:

    用0x0000 (Unicode的零编码)结束字符串。

    DBCS 看上去有点象SBCS。以后我们会看到在串处理和指针使用上是有微妙差别的。字符串"日本语" (nihongo) 的存储格式如下(用LB和TB分别表示前导字节和跟随字节):

    注意,"ni"的值不是WORD值0xFA93。值93和FA顺序组合编码为字符"ni"。(在高位优先CPU中,存放顺序正如上所述)。

    字符串处理函数

    C语言字符串处理函数,如strcpy(), sprintf(), atol()等只能用于单字节字符串。在标准库中有只用于Unicode字符串的函数,如wcscpy(), swprintf(), _wtol()。

    微 软在C运行库(CRT)中加入了对DBCS字符串的支持。对应于strxxx()函数,DBCS使用_mbsxxx()函数。在处理DBCS字符串(如日 语,中文,或其它DBCS)时,就要用_mbsxxx()函数。这些函数也能用于处理SBCS字符串(因为DBCS字符串可能就只含有单字节字符)。

    现在用一个示例来说明字符串处理函数的不同。如有Unicode字符串L"Bob":

    x86 CPU的排列顺序是低位优先(little-endian)的,值0x0042的存储顺序为42 00。这时如用strlen()函数求字符串的长度就发生问题。函数找到第一个字节42,然后是00,意味着字符串结尾,于是返回1。反之,用 wcslen()函数求"Bob"的长度更糟糕。wcslen()首先找到0x6F42,然后是0x0062,以后就在内存缓冲内不断地寻找00 00直至发生一般性保护错(GPF)。

    strxxx()及其对应的_mbsxxx()究竟是如何运作的?二者之间的不同是非常重要的,直接影响到正确遍历DBCS字符串的方法。下面先介绍字符串遍历,然后再回来讨论strxxx()和 _mbsxxx()。

    字符串遍历

    我 们中的大多数人都是从SBCS成长过来的,都习惯于用指针的 ++ 和 -- 操作符来遍历字符串,有时也使用数组来处理字符串中的字符。这二种方法对于SBCS 和 Unicode 字符串的操作都是正确无误的,因为二者的字符都是等长的,编译器能够的正确返回我们寻求的字符位置。

    但对于DBCS字符串就不能这样了。用指针访问DBCS字符串有二个原则,打破这二个原则就会造成错误。

    1. 不可使用 ++ 算子,除非每次都检查是否为前导字节。

    2. 绝不可使用 -- 算子来向后遍历。

    先说明原则2,因为很容易找到一个非人为的示例。假设,有一个配制文件,程序启动时要从安装路径读取该文件,如:C:Program FilesMyCoolAppconfig.bin。文件本身是正常的。

    假设用以下代码来配制文件名:

    bool GetConfigFileName ( char* pszName, size_t nBuffSize )
    {
    char szConfigFilename[MAX_PATH];
        // 这里从注册表读取文件的安装路径,假设一切正常。
    
        // 如果路径末尾没有反斜线,就加上反斜线。
    
        // 首先,用指针指向结尾零:
    
    char* pLastChar = strchr ( szConfigFilename, '' );
        // 然后向后退一个字符:
    
        pLastChar--;  
        if ( *pLastChar != '' )
            strcat ( szConfigFilename, "" );
        // 加上文件名:
    
        strcat ( szConfigFilename, "config.bin" );
        // 如果字符串长度足够,返回文件名:
    
        if ( strlen ( szConfigFilename ) >= nBuffSize )
            return false;
        else
            {
            strcpy ( pszName, szConfigFilename );
            return true;
            }
    }

    这段代码的保护性是很强的,但用到DBCS字符串还是会出错。假如文件的安装路径用日语表达:C:ヨウユソ,该字符串的内存表达为:

    这时用上面的GetConfigFileName()函数来检查文件路径末尾是否含有反斜线就会出错,得到错误的文件名。

    错在哪里?注意上面的二个十六进制值0x5C(蓝色)。前面的0x5C是字符"",后面则是字符值83 5C,代表字符"ソ"。可是函数把它误认为反斜线了。

    正确的方法是用DBCS函数将指针指向恰当的字符位置,如下所示:

    bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )
    {
    char szConfigFilename[MAX_PATH];
        // 这里从注册表读取文件的安装路径,假设一切正常。
    
        // 如果路径末尾没有反斜线,就加上反斜线。
    
        // 首先,用指针指向结尾零:
    
    char* pLastChar = _mbschr ( szConfigFilename, '' );
        // 然后向后退一个双字节字符:
    
        pLastChar = CharPrev ( szConfigFilename, pLastChar );
        if ( *pLastChar != '' )
            _mbscat ( szConfigFilename, "" );
        // 加上文件名:
    
        _mbscat ( szConfigFilename, "config.bin" );
        // 如果字符串长度足够,返回文件名:
    
        if ( _mbslen ( szInstallDir ) >= nBuffSize )
            return false;
        else
            {
            _mbscpy ( pszName, szConfigFilename );
            return true;
            }
    } 

    这个改进的函数用CharPrev() API 函数将指针pLastChar向后移动一个字符。如果字符串末尾的字符是双字节字符,就向后移动2个字节。这时返回的结果是正确的,因为不会将字符误判为反斜线。

    现在可以想像到第一原则了。例如,要遍历字符串寻找字符":",如果不使用CharNext()函数而使用++算子,当跟随字节值恰好也是":"时就会出错。

    与原则2相关的是数组下标的使用:

     2a. 绝不可在字符串数组中使用递减下标。

    出错原因与原则2相同。例如,设置指针pLastChar为:

    char* pLastChar = &szConfigFilename [strlen(szConfigFilename) - 1];

    结果与原则2的出错一样。下标减1就是指针向后移动一个字节,不符原则2。

    再谈 strxxx() _mbsxxx()

    现 在可以清楚为什么要用 _mbsxxx() 函数了。strxxx() 函数不认识DBCS字符而 _mbsxxx()认识。如果调用strrchr("C:", '')函数可能会出错,但 _mbsrchr()认识双字节字符,所以能返回指向最后出现反斜线字符的指针位置。

    最后提一下strxxx() 和 _mbsxxx() 函数族中的字符串长度测量函数,它们都返回字符串的字节数。如果字符串含有3个双字节字符,_mbslen()将返回6。而Unicode的函数返回的是 wchar_ts的数量,如wcslen(L"Bob") 返回3

    C++字符串完全指南 - Win32字符编码(二) 翻译:连波
    15/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098306,00.htm

    Win32 API 中的 MBCS Unicode

    API 的二个字符集

    也 许你没有注意到,Win32的API和消息中的字符串处理函数有二种,一种为MCBS字符串,另一种为Unicode字符串。例如,Win32中没有 SetWindowText()这样的接口,而是用SetWindowTextA()和 SetWindowTextW()函数。后缀A (表示ANSI)指明是MBCS函数,后缀W(表示宽字符)指明是Unicode函数。

    编写Windows程序时,可以选择用MBCS或 Unicode API接口函数。用VC AppWizards向导时,如果不修改预处理器设置,缺省使用的是MBCS函数。但是在API接口中没有SetWindowText()函数,该如何调 用呢?实际上,在winuser.h头文件中做了以下定义:

    BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );
    BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );
    #ifdef UNICODE
     #define SetWindowText  SetWindowTextW
    #else
     #define SetWindowText  SetWindowTextA
    #endif

    编写MBCS应用时,不必定义UNICODE,预处理为:

    #define SetWindowText  SetWindowTextA

    然后将SetWindowText()处理为真正的API接口函数SetWindowTextA() (如果愿意的话,可以直接调用SetWindowTextA() 或SetWindowTextW()函数,不过很少有此需要)。

    如果要将缺省应用接口改为Unicode,就到预处理设置的预处理标记中去掉 _MBCS标记,加入UNICODE 和 _UNICODE (二个标记都要加入,不同的头文件使用不同的标记)。不过,这时要处理普通字符串反而会遇到问题。如有代码:

    HWND hwnd = GetSomeWindowHandle();
    char szNewText[] = "we love Bob!";
    SetWindowText ( hwnd, szNewText );

    编译器将"SetWindowText"置换为"SetWindowTextW"后,代码变为:

    HWND hwnd = GetSomeWindowHandle();
    char szNewText[] = "we love Bob!";
    SetWindowTextW ( hwnd, szNewText );

    看出问题了吧,这里用一个Unicode字符串处理函数来处理单字节字符串。

    第一种解决办法是使用宏定义:
    HWND hwnd = GetSomeWindowHandle();
    #ifdef UNICODE
     wchar_t szNewText[] = L"we love Bob!";
    #else
     char szNewText[] = "we love Bob!";
    #endif
    SetWindowText ( hwnd, szNewText );

    要对每一个字符串都做这样的宏定义显然是令人头痛的。所以用TCHAR来解决这个问题:

    TCHAR的救火角色

    TCHAR 是一种字符类型,适用于MBCS 和 Unicode二种编码。程序中也不必到处使用宏定义。

    TCHAR的宏定义如下:

    #ifdef UNICODE
     typedef wchar_t TCHAR;
    #else
     typedef char TCHAR;
    #endif

    所以,TCHAR中在MBCS程序中是char类型,在Unicode中是 wchar_t 类型。

    对于Unicode字符串,还有个 _T() 宏,用于解决 L 前缀:

    #ifdef UNICODE
     #define _T(x) L##x
    #else
     #define _T(x) x
    #endif

    ## 是预处理算子,将二个变量粘贴在一起。不管什么时候都对字符串用 _T 宏处理,这样就可以在Unicode编码中给字符串加上L前缀,如:

    TCHAR szNewText[] = _T("we love Bob!");

    SetWindowTextA/W 函数族中还有其它隐藏的宏可以用来代替strxxx() 和 _mbsxxx() 字符串函数。例如,可以用 _tcsrchr 宏取代strrchr(),_mbsrchr(),或 wcsrchr()函数。_tcsrchr 根据编码标记为_MBCS 或 UNICODE,将右式函数做相应的扩展处理。宏定义方法类似于SetWindowText。

    不止strxxx()函数族中有TCHAR宏 定义,其它一些函数中也有。例如,_stprintf (取代sprintf()和swprintf()),和 _tfopen (取代fopen() 和 _wfopen())。MSDN的全部宏定义在"Generic-Text Routine Mappings"栏目下。

    String 和 TCHAR 类型定义

    Win32 API 文件中列出的函数名都是通用名(如"SetWindowText"),所有的字符串都按照TCHAR类型处理。(只有XP除外,XP只使用Unicode类型)。下面是MSDN给出的常用类型定义:

     

    类型

    MBCS 编码中的意义

    Unicode 编码中的意义

    WCHAR

    wchar_t

    wchar_t

    LPSTR

    zero-terminated string of char (char* )

    zero-terminated string of char (char* )

    LPCSTR

    constant zero-terminated string of char (constchar* )

    constant zero-terminated string of char (constchar* )

    LPWSTR

    zero-terminated Unicode string (wchar_t* )

    zero-terminated Unicode string (wchar_t* )

    LPCWSTR

    constant zero-terminated Unicode string (const wchar_t* )

    constant zero-terminated Unicode string (const wchar_t* )

    TCHAR

    char

    wchar_t

    LPTSTR

    zero-terminated string of TCHAR (TCHAR* )

    zero-terminated string of TCHAR (TCHAR* )

    LPCTSTR

    constant zero-terminated string of TCHAR (const TCHAR* )

    constant zero-terminated string of TCHAR (const TCHAR* )

    何时使用TCHAR 和Unicode

    可能会有疑问:“为什么要用Unicode?我一直用的都是普通字符串。”

    在三种情况下要用到Unicode:

    1. 程序只运行于Windows NT。
    2. 处理的字符串长于MAX_PATH定义的字符数。
    3. 程序用于Windows XP中的新接口,那里没有A/W版本之分。

    大 部分Unicode API不可用于Windows 9x。所以如果程序要在Windows 9x上运行的话,要强制使用MBCS API (微软推出一个可运行于Windows 9x的新库,叫做Microsoft Layer for Unicode。但我没有试用过,无法说明它的好坏)。相反,NT内部全部使用Unicode编码,使用Unicode API可以加速程序运行。每当将字符串处理为MBCS API时,操作系统都会将字符串转换为Unicode并调用相应的Unicode API 函数。对于返回的字符串,操作系统要做同样的转换。尽管这些转换经过了高度优化,模块尽可能地压缩到最小,但毕竟会影响到程序的运行速度。

    NT 允许使用超长文件名(长于MAX_PATH 定义的260),但只限于Unicode API使用。Unicode API的另外一个优点是程序能够自动处理输入的文字语言。用户可以混合输入英文,中文和日文作为文件名。不必使用其它代码来处理,都按照Unicode编 码方式处理。

    最后,作为Windows 9x的结局,微软似乎抛弃了MBCS API。例如,SetWindowTheme() 接口函数的二个参数只支持Unicode编码。使用Unicode编码省却了MBCS与Unicode之间的转换过程。

    如果程序中还没有使用到Unicode编码,要坚持使用TCHAR和相应的宏。这样不但可以长期保持程序中DBCS编码的安全性,也利于将来扩展使用到Unicode编码。那时只要改变预处理中的设置即可!

    C++字符串完全指南(2) - 各种字符串类(一) 翻译:连波
    19/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098621,00.htm

    前言

    C语言的字符串容易出错,难以管理,并且往往是黑客到处寻找的目标。于是,出现了许多字符串包装类。可惜,人们并不很清楚什么情况下该用哪个类,也不清楚如何将C语言字符串转换到包装类。

    本文涉及到Win32 API,MFC,STL,WTL和Visual C++运行库中使用到的所有的字符串类型。说明各个类的用法,如何构造对象,如何进行类转换等等。Nish为本文提供了Visual C++ 7的managed string 类的用法。

    阅读本文之前,应完全理解本指南第一部分中阐述的字符类型和编码。

    字符串类的首要原则:

    不要随便使用类型强制转换,除非转换的类型是明确由文档规定的。

    之 所以撰写字符串指南这二篇文章,是因为常有人问到如何将X类型的字符串转换到Z类型。提问者使用了强制类型转换(cast),但不知道为什么不能转换成 功。各种各样的字符串类型,特别是BSTR,在任何场合都不是三言二语可以讲清的。因此,我以为这些提问者是想让强制类型转换来处理一切。

    除非明确规定了转换算子,不要将任何其它类型数据强制转换为string。一个字符串不能用强制类型转换到string类。例如:

    void SomeFunc ( LPCWSTR widestr );
    main()
    {
    SomeFunc ( (LPCWSTR) "C:foo.txt" ); // 错!
    }

    这段代码100%错误。它可以通过编译,因为类型强制转换超越了编译器的类型检验。但是,能够通过编译,并不证明代码是正确的。

    下面,我将指出什么时候用类型强制转换是合理的。
    C 语言字符串与类型定义

    如指南的第一部分所述,Windows API定义了TCHAR术语。它可用于MBCS或Unicode编码字符,取决于预处理设置为_MBCS 或 _UNICODE标记。关于TCHAR的详细说明请阅指南的第一部分。为便于叙述,下面给出字符类型定义:

    Type

    Meaning

    WCHAR

    Unicode character (wchar_t )

    TCHAR

    MBCS or Unicode character, depending on preprocessor settings

    LPSTR

    string of char (char* )

    LPCSTR

    constant string of char (constchar* )

    LPWSTR

    string of WCHAR (WCHAR* )

    LPCWSTR

    constant string of WCHAR (const WCHAR* )

    LPTSTR

    string of TCHAR (TCHAR* )

    LPCTSTR

    constant string of TCHAR (const TCHAR* )

    另 外还有一个字符类型OLECHAR。这是一种对象链接与嵌入的数据类型(比如嵌入Word文档)。这个类型通常定义为wchar_t。如果将预处理设置定 义为OLE2ANSI,OLECHAR将被定义为char类型。现在已经不再定义OLE2ANSI(它只在MFC 3以前版本中使用),所以我将OLECHAR作为Unicode字符处理。

    下面是与OLECHAR相关的类型定义:

    Type

    Meaning

    OLECHAR

    Unicode character (wchar_t )

    LPOLESTR

    string of OLECHAR (OLECHAR* )

    LPCOLESTR

    constant string of OLECHAR (const OLECHAR* )

    还有以下二个宏让相同的代码能够适用于MBCS和Unicode编码:

    Type

    Meaning

    _T(x)

    Prepends L to the literal in Unicode builds.

    OLESTR(x)

    Prepends L to the literal to make it an LPCOLESTR .

    宏_T有几种形式,功能都相同。如: -- TEXT, _TEXT, __TEXT, 和 __T这四种宏的功能相同。

    COM 中的字符串 - BSTR VARIANT

    许多COM接口使用BSTR声明字符串。BSTR有一些缺陷,所以我在这里让它独立成章。

    BSTR 是Pascal类型字符串(字符串长度值显式地与数据存放在一起)和C类型字符串(字符串长度必须通过寻找到结尾零字符来计算)的混合型字符串。BSTR 属于Unicode字符串,字符串中预置了字符串长度值,并且用一个零字符来结尾。下面是一个"Bob"的BSTR字符串:

    注 意,字符串长度值是一个DWORD类型值,给出字符串的字节长度,但不包括结尾零。在上例,"Bob"含有3个Unicode字符(不计结尾零),6个字 节长。因为明确给出了字符串长度,所以当BSTR数据在不同的处理器和计算机之间传送时,COM库能够知道应该传送的数据量。

    附带说一下,BSTR可以包含任何数据块,不单是字符。它甚至可以包容内嵌零字符数据。这些不在本文讨论范围。

    C++中的BSTR变量其实就是指向字符串首字符的指针。BSTR是这样定义的:

    typedef OLECHAR* BSTR;

    这 个定义很糟糕,因为事实上BSTR与Unicode字符串不一样。有了这个类型定义,就越过了类型检查,可以混合使用LPOLESTR和BSTR。向一个 需要LPCOLESTR (或 LPCWSTR)类型数据的函数传递BSTR数据是安全的,反之则不然。所以要清楚了解函数所需的字符串类型,并向函数传递正确类型的字符串。

    要 知道为什么向一个需要BSTR类型数据的函数传递LPCWSTR类型数据是不安全的,就别忘了BSTR必须在字符串开头的四个字节保留字符串长度值。但 LPCWSTR字符串中没有这个值。当其它的处理过程(如Word)要寻找BSTR的长度值时就会找到一堆垃圾或堆栈中的其它数据或其它随机数据。这就导 致方法失效,当长度值太大时将导致崩溃。

    许多应用接口都使用BSTR,但都用到二个最重要的函数来构造和析构BSTR。就是 SysAllocString()和SysFreeString()函数。SysAllocString()将Unicode字符串拷贝到 BSTR,SysFreeString()释放BSTR。示例如下:

    BSTR bstr = NULL;
    bstr = SysAllocString ( L"Hi Bob!" );
    if ( NULL == bstr )
    // 内存溢出
    // 这里使用bstr

    SysFreeString ( bstr );

    当然,各种BSTR包装类都会小心地管理内存。

    自动接口中的另一个数据类型是VARIANT。它用于在无类型语言,诸如 JScript,VBScript,以及Visual Basic,之间传递数据。VARIANT可以包容许多不用类型的数据,如long和IDispatch*。如果VARIANT包含一个字符串,这个字符 串是BSTR类型。在下文的VARIANT包装类中我还会谈及更多的VARIANT。
    C++字符串完全指南(2) - 各种字符串类- CRT类 翻译:连波
    20/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098682,00.htm

    _bstr_t

    字符串包装类

    我已经说明了字符串的各种类型,现在讨论包装类。对于每个包装类,我都会说明它的对象构造过程和如何转换成C类型字符串指针。应用接口的调用,或构造另一个不同类型的字符串类,大多都要用到C类型指针。本文不涉及类的其它操作,如排序和比较等。

    再强调一下,在完全了解转换结果之前不要随意使用强制类型转换。

    CRT类

    _bstr_t

    _bstr_t 是BSTR的完全包装类。实际上,它隐含了BSTR。它提供多种构造函数,能够处理隐含的C类型字符串。但它本身却不提供BSTR的处理机制,所以不能作 为COM方法的输出参数[out]。如果要用到BSTR* 类型数据,用ATL的CComBSTR类更为方便。

    _bstr_t 数据可以传递给需要BSTR数据的函数,但必须满足以下三个条件:

    首先 ,_bstr_t 具有能够转换为wchar_t*类型数据的函数。

    其次 ,根据BSTR定义,使得wchar_t* 和BSTR对于编译器来说是相同的。

    第三 ,_bstr_t内部保留的指向内存数据块的指针 wchar_t* 要遵循BSTR格式。

    满足这些条件,即使没有相应的BSTR转换文档,_bstr_t 也能正常工作。示例如下:

     // 构造
    
    _bstr_t bs1 = "char string"; // 从LPCSTR构造
    _bstr_t bs2 = L"wide char string"; // 从LPCWSTR构造
    _bstr_t bs3 = bs1; // 拷贝另一个 _bstr_t
    _variant_t v = "Bob";
    _bstr_t bs4 = v; // 从一个含有字符串的 _variant_t 构造
    // 数据萃取
    LPCSTR psz1 = bs1; // 自动转换到MBCS字符串
    LPCSTR psz2 = (LPCSTR) bs1; // cast OK, 同上
    LPCWSTR pwsz1 = bs1; // 返回内部的Unicode字符串
    LPCWSTR pwsz2 = (LPCWSTR) bs1; // cast OK, 同上
    BSTR bstr = bs1.copy(); // 拷贝bs1, 返回BSTR
    // ...
    SysFreeString ( bstr );

    注意,_bstr_t 也可以转换为char* 和 wchar_t*。这是个设计问题。虽然char* 和 wchar_t*不是常量指针,但不能用于修改字符串,因为可能会打破内部BSTR结构。

    _variant_t
    _variant_t

    _variant_t 是VARIANT的完全包装类。它提供多种构造函数和数据转换函数。本文仅讨论与字符串有关的操作。

    // 构造
    
    _variant_t v1 = "char string"; // 从LPCSTR 构造
    _variant_t v2 = L"wide char string"; // 从LPCWSTR 构造
    _bstr_t bs1 = "Bob";
    _variant_t v3 = bs1; // 拷贝一个 _bstr_t 对象
    // 数据萃取
    _bstr_t bs2 = v1; // 从VARIANT中提取BSTR
    _bstr_t bs3 = (_bstr_t) v1; // cast OK, 同上

    注意,_variant_t 方法在转换失败时会抛出异常,所以要准备用catch 捕捉_com_error异常。

    另外要注意 _variant_t 不能直接转换成MBCS字符串。要建立一个过渡的_bstr_t 变量,用其它提供转换Unicode到MBCS的类函数,或ATL转换宏来转换。

    与_bstr_t 不同,_variant_t 数据可以作为参数直接传送给COM方法。_variant_t 继承了VARIANT类型,所以在需要使用VARIANT的地方使用_variant_t 是C++语言规则允许的。
    C++字符串完全指南(2) - STL和ATL类 翻译:连波
    21/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098845,00.htm

    STL类

    STL类

    STL 只有一个字符串类,即basic_string。basic_string管理一个零结尾的字符数组。字符类型由模板参数决定。通 常,basic_string被处理为不透明对象。可以获得一个只读指针来访问缓冲区,但写操作都是由basic_string的成员函数进行的。

    basic_string预定义了二个特例:string,含有char类型字符;which,含有wchar_t类型字符。没有内建的TCHAR特例,可用下面的代码实现:

    // 特例化
    
    typedef basic_string tstring; // TCHAR字符串
    // 构造
    string str = "char string"; // 从LPCSTR构造
    wstring wstr = L"wide char string"; // 从LPCWSTR构造
    tstring tstr = _T("TCHAR string"); // 从LPCTSTR构造
    // 数据萃取
    LPCSTR psz = str.c_str(); // 指向str缓冲区的只读指针
    LPCWSTR pwsz = wstr.c_str(); // 指向wstr缓冲区的只读指针
    LPCTSTR ptsz = tstr.c_str(); // 指向tstr缓冲区的只读指针

    与_bstr_t 不同,basic_string不能在字符集之间进行转换。但是如果一个构造函数接受相应的字符类型,可以将由c_str()返回的指针传递给这个构造函数。例如:

    // 从basic_string构造_bstr_t
     
    _bstr_t bs1 = str.c_str(); // 从LPCSTR构造 _bstr_t
    _bstr_t bs2 = wstr.c_str(); // 从LPCWSTR构造 _bstr_t
    ATL类
    CComBSTR

    CComBSTR 是ATL的BSTR包装类。某些情况下比_bstr_t 更有用。最主要的是,CComBSTR允许操作隐含BSTR。就是说,传递一个CComBSTR对象给COM方法时,CComBSTR对象会自动管理BSTR内存。例如,要调用下面的接口函数:

    // 简单接口
    
    struct IStuff : public IUnknown
    {
    // 略去COM程序...
    STDMETHOD(SetText)(BSTR bsText);
    STDMETHOD(GetText)(BSTR* pbsText);
    };

    CComBSTR 有一个BSTR操作方法,能将BSTR直接传递给SetText()。还有一个引用操作(operator &)方法,返回BSTR*,将BSTR*传递给需要它的有关函数。

    CComBSTR bs1;
    CComBSTR bs2 = "new text";
    pStuff->GetText ( &bs1 ); // ok, 取得内部BSTR地址
    pStuff->SetText ( bs2 ); // ok, 调用BSTR转换
    pStuff->SetText ( (BSTR) bs2 ); // cast ok, 同上

    CComVariant
    CComBSTR有类似于 _bstr_t 的构造函数。但没有内建MBCS字符串的转换函数。可以调用ATL宏进行转换。

    // 构造
    
    CComBSTR bs1 = "char string"; // 从LPCSTR构造
    CComBSTR bs2 = L"wide char string"; // 从LPCWSTR构造
    CComBSTR bs3 = bs1; // 拷贝CComBSTR
    CComBSTR bs4;
    bs4.LoadString ( IDS_SOME_STR ); // 从字符串表加载
    // 数据萃取
    BSTR bstr1 = bs1; // 返回内部BSTR,但不可修改!
    BSTR bstr2 = (BSTR) bs1; // cast ok, 同上
    BSTR bstr3 = bs1.Copy(); // 拷贝bs1, 返回BSTR
    BSTR bstr4;
    bstr4 = bs1.Detach(); // bs1不再管理它的BSTR
    // ...
    SysFreeString ( bstr3 );
    SysFreeString ( bstr4 );

    上面的最后一个示例用到了Detach()方法。该方法调用后,CComBSTR对象就不再管理它的BSTR或其相应内存。所以bstr4就必须调用SysFreeString()。

    最 后讨论一下引用操作符(operator &)。它的超越使得有些STL集合(如list)不能直接使用CComBSTR。在集合上使用引用操作返回指向包容类的指针。但是在 CComBSTR上使用引用操作,返回的是BSTR*,不是CComBSTR*。不过可以用ATL的CAdapt类来解决这个问题。例如,要建立一个 CComBSTR的队列,可以声明为:

      std::list< CAdapt> bstr_list;

    CAdapt 提供集合所需的操作,是隐含于代码的。这时使用bstr_list 就象在操作一个CComBSTR队列。

    CComVariant

    CComVariant 是VARIANT的包装类。但与 _variant_t 不同,它的VARIANT不是隐含的,可以直接操作类里的VARIANT成员。CComVariant 提供多种构造函数和多类型操作。这里只介绍与字符串有关的操作。

    // 构造
    
    CComVariant v1 = "char string"; // 从LPCSTR构造
    CComVariant v2 = L"wide char string"; // 从LPCWSTR构造
    CComBSTR bs1 = "BSTR bob";
    CComVariant v3 = (BSTR) bs1; // 从BSTR拷贝
    // 数据萃取
    CComBSTR bs2 = v1.bstrVal; // 从VARIANT提取BSTR

    跟_variant_t 不同,CComVariant没有不同VARIANT类型之间的转换操作。必须直接操作VARIANT成员,并确定该VARIANT的类型无误。调用ChangeType()方法可将CComVariant数据转换为BSTR。

    CComVariant v4 = ... // 从某种类型初始化 v4
    
    CComBSTR bs3;
    if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ))
    bs3 = v4.bstrVal;

    跟 _variant_t 一样,CComVariant不能直接转换为MBCS字符串。要建立一个过渡的_bstr_t 变量,用其它提供转换Unicode到MBCS的类函数,或ATL转换宏来转换。

    ATL转换宏

    ATL转换宏

    ATL的字符串转换宏可以方便地转换不同编码的字符,用在函数中很有效。宏按照[source type]2[new type] 或 [source type]2C[new type]格式命名。后者转换为一个常量指针 (名字内含"C")。类型缩写如下:


     A:MBCS字符串,char* (A for ANSI)
     W:Unicode字符串,wchar_t* (W for wide)
     T:TCHAR字符串,TCHAR*
     OLE:OLECHAR字符串,OLECHAR* (实际等于W)
     BSTR:BSTR (只用于目的类型)

    例如,W2A() 将Unicode字符串转换为MBCS字符串,T2CW()将TCHAR字符串转换为Unicode字符串常量。

    要使用宏转换,程序中要包含atlconv.h头文件。可以在非ATL程序中使用宏转换,因为头文件不依赖其它的ATL,也不需要 _Module全局变量。如在函数中使用转换宏,在函数起始处先写上USES_CONVERSION宏。它表明某些局部变量由宏控制使用。

    转换得到的结果字符串,只要不是BSTR,都存储在堆栈中。如果要在函数外使用这些字符串,就要将这些字符串拷贝到其它的字符串类。如果结果是BSTR,内存不会自动释放,因此必须将返回值分配给一个BSTR变量或BSTR的包装类,以避免内存泄露。

    下面是若干宏转换示例:

    // 带有字符串的函数:
    
    void Foo ( LPCWSTR wstr );
    void Bar ( BSTR bstr );
    // 返回字符串的函数:
    void Baz ( BSTR* pbstr );
    #include
    main()
    {
    using std::string;
    USES_CONVERSION; // 声明局部变量由宏控制使用
    // 示例1:送一个MBCS字符串到Foo()
    LPCSTR psz1 = "Bob";
    string str1 = "Bob";
    Foo ( A2CW(psz1) );
    Foo ( A2CW(str1.c_str()) );
    // 示例2:将MBCS字符串和Unicode字符串送到Bar()
    LPCSTR psz2 = "Bob";
    LPCWSTR wsz = L"Bob";
    BSTR bs1;
    CComBSTR bs2;
    bs1 = A2BSTR(psz2); // 创建 BSTR
    bs2.Attach ( W2BSTR(wsz) ); // 同上,分配到CComBSTR
    Bar ( bs1 );
    Bar ( bs2 );
    SysFreeString ( bs1 ); // 释放bs1
    // 不必释放bs2,由CComBSTR释放。
    // 示例3:转换由Baz()返回的BSTR
    BSTR bs3 = NULL;
    string str2;
    Baz ( &bs3 ); // Baz() 填充bs3内容
    str2 = W2CA(bs3); // 转换为MBCS字符串
    SysFreeString ( bs3 ); // 释放bs3
    }

    可以看到,向一个需要某种类型参数的函数传递另一种类型的参数,用宏转换是非常方便的。
    C++字符串完全指南(2) - MFC类 翻译:连波
    22/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098983,00.htm

    MFC类

    MFC类

    CString

    MFC 的CString含有TCHAR,它的实际字符类型取决于预处理标记的设置。通常,CString象STL字符串一样是不透明对象,只能用CString 的方法来修改。CString比STL字符串更优越的是它的构造函数接受MBCS和Unicode字符串。并且可以转换为LPCTSTR,因此可以向接受 LPCTSTR的函数直接传递CString对象,不必调用c_str()方法。

    // 构造
    CString s1 = "char string"; // 从LPCSTR构造
    CString s2 = L"wide char string"; // 从LPCWSTR构造
    CString s3 ( ' ', 100 ); // 预分配100字节,填充空格
    CString s4 = "New window text";
    // 可以在LPCTSTR处使用CString:
    SetWindowText ( hwndSomeWindow, s4 );
    // 或者,显式地做强制类型转换:
    SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 );

    也可以从字符串表加载字符串。CString通过LoadString()来构造对象。用Format()方法可有选择地从字符串表读取一定格式的字符串。

    // 从字符串表构造/加载
    CString s5 ( (LPCTSTR) IDS_SOME_STR ); // 从字符串表加载
    CString s6, s7;
    // 从字符串表加载
    s6.LoadString ( IDS_SOME_STR );
    // 从字符串表加载打印格式的字符串
    s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... );

    第一个构造函数看上去有点怪,但它的确是文档标定的字符串加载方式。

    注意,CString只允许一种强制类型转换,即强 制转换为LPCTSTR。强制转换为LPTSTR (非常量指针)是错误的。按照老习惯,将CString强制转换为LPTSTR只能伤害自己。有时在程序中没有发现出错,那只是碰巧。转换到非常量指针的 正确方法是调用GetBuffer()方法。

    下面以往队列加入元素为例说明如何正确地使用CString:

    CString str = _T("new text");
    LVITEM item = {0};
    item.mask = LVIF_TEXT;
    item.iItem = 1;
    item.pszText = (LPTSTR)(LPCTSTR) str; // 错!
    item.pszText = str.GetBuffer(0); // 正确
    ListView_SetItem ( &item );
    str.ReleaseBuffer(); // 将队列返回给str

    pszText成员是LPTSTR,一个非常量指针,因此要用str的GetBuffer()。GetBuffer()的参数是CString分配的最小缓冲区。如果要分配一个1K的TCHAR,调用GetBuffer(1024)。参数为0,只返回指向字符串的指针。

    上 面示例的出错语句可以通过编译,甚至可以正常工作,如果恰好就是这个类型。但这不证明语法正确。进行非常量的强制类型转换,打破了面向对象的封装原则,并 逾越了CString的内部操作。如果你习惯进行这样的强制类型转换,终会遇到出错,可你未必知道错在何处,因为你到处都在做这样的转换,而代码也都能运 行。

    知道为什么人们总在抱怨有缺陷的软件吗?不正确的代码就臭虫的滋生地。然道你愿意编写明知有错的代码让臭虫有机可乘?还是花些时间学习CString的正确用法让你的代码能够100%的正确吧。

    CString还有二个函数能够从CString中得到BSTR,并在必要时转换成Unicode。那就是AllocSysString()和SetSysString()。除了SetSysString()使用BSTR*参数外,二者一样。

    // 转换成BSTR
    CString s5 = "Bob!";
    BSTR bs1 = NULL, bs2 = NULL;
    bs1 = s5.AllocSysString();
    s5.SetSysString ( &bs2 );
    // ...
    SysFreeString ( bs1 );
    SysFreeString ( bs2 );

    COleVariant 与CComVariant 非常相似。COleVariant 继承于VARIANT,可以传递给需要VARIANT的函数。但又与CComVariant 不同,COleVariant 只有一个LPCTSTR的构造函数,不提供单独的LPCSTR和LPCWSTR的构造函数。在大多情况下,没有问题,因为总是愿意把字符串处理为 LPCTSTR。但你必须知道这点。COleVariant 也有接受CString的构造函数。

    // 构造
    CString s1 = _T("tchar string");
    COleVariant v1 = _T("Bob"); // 从LPCTSTR构造
    COleVariant v2 = s1; // 从CString拷贝

    对于CComVariant,必须直接处理VARIANT成员,用ChangeType()方法在必要时将其转换为字符串。但是,COleVariant::ChangeType() 在转换失败时会抛出异常,而不是返回HRESULT的出错码。

    // 数据萃取
    COleVariant v3 = ...; // 从某种类型构造v3
    BSTR bs = NULL;
    try
    {
    v3.ChangeType ( VT_BSTR );
    bs = v3.bstrVal;
    }
    catch ( COleException* e )
    {
    // 出错,无法转换
    }
    SysFreeString ( bs );

    WTL类

    WTL类

    CString

    WTL的CString与MFC的CString的行为完全相同,参阅上面关于MFC CString的说明即可。

    CLR 及 VC 7 类

    System::String 是.NET的字符串类。在其内部,String对象是一个不变的字符序列。任何操作String对象的String方法都返回一个新的String对象, 因为原有的String对象要保持不变。String类有一个特性,当多个String都指向同一组字符集时,它们其实是指向同一个对象。Managed Extensions C++ 的字符串有一个新的前缀S,用来表明是一个managed string字符串。

    // 构造
    String* ms = S"This is a nice managed string";

    可以用unmanaged string字符串来构造String对象,但不如用managed string构造String对象有效。原因是所有相同的具有S前缀的字符串都指向同一个对象,而unmanaged string没有这个特点。下面的例子可以说明得更清楚些:

    String* ms1 = S"this is nice";
    String* ms2 = S"this is nice";
    String* ms3 = L"this is nice";
    Console::WriteLine ( ms1 == ms2 ); // 输出true
    Console::WriteLine ( ms1 == ms3); // 输出false

    要与没有S前缀的字符串做比较,用String::CompareTo()方法来实现,如:

      Console::WriteLine ( ms1->CompareTo(ms2) );
    Console::WriteLine ( ms1->CompareTo(ms3) );

    二者都输出0,说明字符串相等。

    在String和MFC 7的CString之间转换很容易。CString可以转换为LPCTSTR,String有接受char* 和 wchar_t* 的二种构造函数。因此可以直接把CString传递给String的构造函数:

      CString s1 ( "hello world" );
    String* s2 ( s1 ); // 从CString拷贝

    反向转换的方法也类似:

      String* s1 = S"Three cats";
    CString s2 ( s1 );

    可能有点迷惑。从VS.NET开始,CString有一个接受String对象的构造函数,所以是正确的。

      CStringT ( System::String* pString );

    为了加速操作,有时可以用基础字符串(underlying string):

    String* s1 = S"Three cats";
    Console::WriteLine ( s1 );
    const __wchar_t __pin* pstr = PtrToStringChars(s1);
    for ( int i = 0; i < wcslen(pstr); i++ )
    (*const_cast<__wchar_t*>(pstr+i))++;
    Console::WriteLine ( s1 );

    PtrToStringChars() 返回指向基础字符串的 const __wchar_t* 指针,可以防止在操作字符串时,垃圾收集器去除该字符串。
    C++字符串完全指南(2) - 总结 翻译:连波
    23/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39099061,00.htm

    字符串类的打印格式函数

    对字符串包装类使用printf()或其它类似功能的函数时要特别小心。包括sprintf()函数及其变种,以及TRACE 和ATLTRACE 宏。它们的参数都不做类型检验,一定要给它们传递C语言字符串,而不是整个string对象。

    例如,要向ATLTRACE()传递一个_bstr_t 里的字符串,必须显式用(LPCSTR)或 (LPCWSTR)进行强制类型转换:


      _bstr_t bs = L"Bob!";
    ATLTRACE("The string is: %s in line %dn", (LPCSTR) bs, nLine);

    如果忘了用强制类型转换,直接把整个 _bstr_t 对象传递给ATLTRACE,跟踪消息将输出无意义的东西,因为_bstr_t 变量内的所有数据都进栈了。

    所有类的总结

    常用的字符串类之间的转换方法是:将源字符串转换为C类型字符串指针,然后将该指针传递给目标类的构造函数。下面列出将字符串转换为C类型指针的方法,以及哪些类的构造函数接受C类型指针。

    Class

    string
    type

    convert to char* ?

    convert to constchar* ?

    convert to wchar_t* ?

    convert to const wchar_t* ?

    convert to BSTR ?

    construct from char* ?

    construct from wchar_t* ?

    _bstr_t

    BSTR

    yes, cast1

    yes, cast

    yes, cast1

    yes, cast

    yes2

    yes

    yes

    _variant_t

    BSTR

    no

    no

    no

    cast to
    _bstr_t 3

    cast to
    _bstr_t 3

    yes

    yes

    string

    MBCS

    no

    yes, c_str() method

    no

    no

    no

    yes

    no

    wstring

    Unicode

    no

    no

    no

    yes, c_str() method

    no

    no

    yes

    CComBSTR

    BSTR

    no

    no

    no

    yes, cast
    to BSTR

    yes, cast

    yes

    yes

    CComVariant

    BSTR

    no

    no

    no

    yes4

    yes4

    yes

    yes

    CString

    TCHAR

    no6

    in MBCS
    builds, cast

    no6

    in Unicode
    builds, cast

    no5

    yes

    yes

    COleVariant

    BSTR

    no

    no

    no

    yes4

    yes4

    in MBCS builds

    in Unicode builds

    附注:

    1. 虽然 _bstr_t 可以转换为非常量指针,但对内部缓冲区的修改可能导致内存溢出,或在释放BSTR时导致内存泄露。
    2. bstr_t 的BSTR内含 wchar_t* 变量,所以可将const wchar_t* 转换到BSTR。但这个用法将来可能会改变,使用时要小心。
    3. 如果转换到BSTR失败,将抛出异常。
    4. 用ChangeType()处理VARIANT的bstrVal。在MFC,转换失败将抛出异常。
    5. 虽然没有BSTR的转换函数,但AllocSysString()可返回一个新的BSTR。
    6. 用GetBuffer()方法可临时得到一个非常量TCHAR指针。


    本文引用通告地址: http://blog.csdn.net/venuszhou/services/trackbacks/299754.aspx

    - 作者: largedong 2005年10月19日, 星期三 14:35  回复(0) |  引用(0) 加入博采

    状态栏弹出窗口

    OnCreate中添加

    ShowWindow( SW_HIDE );
    ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW);

    UpdateWindow ();

    //以上代码 为了不在任务栏显示
    AnimateWindow( 1000, AW_VER_NEGATIVE );//滑动

    OnClose中添加

    AnimateWindow( 1000, AW_HIDE|AW_VER_NEGATIVE );

    起一个定时器 让窗口自杀

    - 作者: largedong 2005年10月11日, 星期二 17:40  回复(0) |  引用(0) 加入博采

    找出托盘区的坐标
    可以使用FindWindow来找到底部的任务条窗口,然后通过列举子窗口的方法来找到托盘区窗口,任务条窗口的类名是“Shell_TrayWnd”,托盘区窗口的类名是TrayNotifyWnd。

    BOOL CALLBACK FindTrayWnd(HWND hwnd, LPARAM lParam)
    {
    TCHAR szClassName[256];
    GetClassName(hwnd, szClassName, 255);

    // 比较窗口类名
    if (_tcscmp(szClassName, _T("TrayNotifyWnd")) == 0)
    {
    CRect *pRect = (CRect*) lParam;
    ::GetWindowRect(hwnd, pRect);
    return TRUE;
    }

    // 当找到时钟窗口时表示可以结束了
    if (_tcscmp(szClassName, _T("TrayClockWClass")) == 0)
    {
    CRect *pRect = (CRect*) lParam;
    CRect rectClock;
    ::GetWindowRect(hwnd, rectClock);
    pRect->right = rectClock.left;
    return FALSE;
    }

    return TRUE;
    }

    CRect GetTrayWndRect()
    {
    CRect rect(0,0,0,0);

    // 查找托盘窗口
    CWnd* pWnd = FindWindow(_T("Shell_TrayWnd"), NULL);
    if (pWnd)
    {
    //通过列举子窗口来进行查找
    EnumChildWindows(pWnd->m_hWnd, FindTrayWnd, (LPARAM)&rect);
    pWnd->GetWindowRect(rect);
    //rect 为托盘区矩形
    }
    return rect;

    - 作者: largedong 2005年10月11日, 星期二 16:40  回复(0) |  引用(0) 加入博采

    动画窗口的实现(AnimateWindow函数)-

    要 实现这种动画窗口的编程效果,主要用到Windows API中的AnimateWindow函数,通过在窗口的创建或消毁过程中运用该函数,来实现开启 和关闭程序时达到所希望的动画窗口效果。AnimateWindow函数所提供的动画效果十分丰富,我们可以在自己的程序中选择各种不同的动画效果,增强 程序的趣味性。为使读者对AnimateWindow函数有一个基本了解,我们先对该函数做一个简单介绍:

    函数原型:BOOL AnimateWindow(HWND hWnd,DWORD dwTime,DWORD dwFlags)。

    函数功能:该函数能在显示与隐藏窗口时产生两种特殊类型的动画效果:滚动动画和滑动动画。

    参数含义:

    hWnd:指定产生动画的窗口的句柄。

    dwTime:指明动画持续的时间(以微秒计),完成一个动画的标准时间为200微秒。

    dwFags:指定动画类型。这个参数可以是一个或多个下列标志的组合。标志描述:

    AW_SLIDE:使用滑动类型。缺省则为滚动动画类型。当使用AW_CENTER标志时,这个标志就被忽略。

    AW_ACTIVATE:激活窗口。在使用了AW_HIDE标志后不能使用这个标志。

    AW_BLEND:实现淡出效果。只有当hWnd为顶层窗口的时候才可以使用此标志。

    AW_HIDE:隐藏窗口,缺省则显示窗口。

    AW_CENTER:若使用了AW_HIDE标志,则使窗口向内重叠,即收缩窗口;若未使用AW_HIDE标志,则使窗口向外扩展,即展开窗口。

    AW_HOR_POSITIVE:自左向右显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。

    AW_VER_POSITIVE:自顶向下显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。

    AW_VER_NEGATIVE:自下向上显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。

    返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。在下列情况下函数将失败:

    窗口使用了窗口边界;窗口已经可见仍要显示窗口;窗口已经隐藏仍要隐藏窗口。

    三、动画窗口的实现

    下面就以一个简单的单文档程序为例,说明如何在VC中使用AnimateWindow函数来实现打开和关闭程序时的动画效果。基于多文档与对话框的程序所用方法类似,本文就不一一介绍。笔者所使用的开发环境为:

    1.WindowsXP,Visual C++6。

    1、建立一个MFC AppWizard(exe)应用工程Animate。

    在MFC AppWizard向导的第一步中选择Single document,再点击按键Finish->OK完成工程建立。

    2、在CMainFrame::OnCreate函数中增加黑体加粗部分语句。

    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    { ……
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndToolBar);
    AnimateWindow(GetSafeHwnd(),1000,AW_CENTER);
    return 0;
    }

    3、使用MFC ClassWizard增加消息处理函数

    使用ClassWizard在CMainFrame类中增加WM_CLOSE消息处理函数,并增加以下语句。

    void CMainFrame::OnClose()
    {// TODO: Add your message handler code here and/or call default
    AnimateWindow(GetSafeHwnd(),1000,AW_HIDE|AW_CENTER);
    CFrameWnd::OnClose();
    }

    2.WindowsXP,vc.net2003

    创建步骤相同

    四、编译时出现的问题

    在实现动画窗口的程序时,笔者发现如果直接在VC中使用AnimateWindow函数,在编译时会报告出错。以上述程序为例,在编译时系统会报告:

    在环境1,2下都会出现相同的问题

    通 过错误提示可以看出是编译系统认为AnimateWindow函数和AW_HIDE、AW_CENTER两个参数没有定义。因该函数是定义在 Winuser.h头文件中的,于时,笔者显示地在程序中定义了对该头文件的包含,编译时却仍然出现相同的错误。为什么在VC中编译不能通过呢?通过查阅 MSDN笔者发现在MSDN中明确提到WindowsNT5.0和Windows98以上版本均支持该函数。通过笔者的研究发现,问题出在定义 AnimateWindow函数的头文件Winuser.h中,在VC安装目录下进入include子目录,用EDIT打开Winuser.h文件,按 F3键查找AnimateWindow,可以发现有两处定义,一处是定义该函数中使用到的参数;另一处是该函数原型的定义。在这两处定义中均出现了对 Windows版本的条件判断,#if (WINVER >= 0X500)……,原来问题出在这里,我们目前所使用的各种Windows主版本号均为5点零以下,所以在VC中编译上述程序时,编译系统自然将 AnimateWindow函数排除在外。因此为了在我们的程序中使用该函数,就得对其头文件进行一些小小的修改,即将#if (WINVER >= 0X500)改为#if (WINVER >= 0X400),请注意两处出现该函数定义的部分都要进行修改。

    修改好头文件后,编译即可通过。

    在环境1下,(1)添加头文件#include"winuser.h"

    (2)如上所说的改变版本.

    问题就可以得到解决

    在环境2下,我同样添加了头文件,并且同样修改了版本,可是问题并没有得到解决,还会同样的错,仍报错

    'AnimateWindow' : undeclared identifier //错误编号C3861

    'AW_HIDE' : undeclared identifier //错误编号C2065

    'AW_CENTER' : undeclared identifier

    真服了,不知道怎么样解决了

    五、小结

    实际上在AnimateWindow函数中包含了多种动画效果,我们只须在程序中增加几条语句,就能使得程序的开启和关闭就得生动有趣。

    另 外,通过本例我们可以发现,尽管在MSDN中明确指出在Windows98中支持AnimateWindow函数,但是在实际编程时却发现该函数在头文件 中的定义却将版本控制在了5.0以上,笔者虽然不知道原因何在,但本文对此问题的解决无疑是一种比较新颖的作法,希望能够对广大读者的工作起到抛砖引玉的 效果。

    另:2005.9.3在我看到PRM的评论后,重新做了一遍实验,却发现在环境一和环境二下都没有碰到任何问题。也邪门,想当初调试 这个函数是屡屡受挫。确实在vc++6中windows.h的定义版本为(#define WINVER 0x0400),而在vc.net2003中(#define WINVER 0x0501)。所以两个编译环境默认版本为不一样的。当然可以采用PRM的方法,可是并不是每一个人都会碰到这样的错误,譬如我刚才重做了一遍,什么错 误都没有。万一哪天又用到这个函数,碰到这个问题可以到网上多搜几种方法,看看别人又什么高招!

    - 作者: largedong 2005年10月8日, 星期六 17:10  回复(0) |  引用(0) 加入博采

    了解CObject 和 CRuntimeClass
    作者:林水怒 shuinu@163.net
    转载请保留作者署名

    CObject和CRuntimeClass是MFC中两个非常重要的类/结构,绝大部分MFC类都是以CObject做为基类, CRuntimeClass结构同CObject密不可分,了解它们对于深入理解MFC具有重要意义。
    一、CRuntimeClass结构
    要理解CObject,我们先来看一下CRuntimeClass这个在MFC中至关重要的一个结构。
    每个从CObject中派生的类都有有一个CRuntimeClass对象同它关联以完成在运行时得到类实例的信息或者是它的基类。 在afx.h中它的定义如下:
    struct CRuntimeClass
    {
    // Attributes
    LPCSTR m_lpszClassName; //类名,一般是指包含CRuntimeClass对象的类的名称
    int m_nObjectSize; //包含CRuntimeClass对象的类sizeof的大小,不包括它分配的内存
    UINT m_wSchema; // schema number of the loaded class
    CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class 指向一个建立实例的构造函数

    #ifdef _AFXDLL// Call this when using MFC in a shared DLL
    CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
    #else
    CRuntimeClass* m_pBaseClass;
    #endif

    //以上m_pBaseClass的指针(函数)是MFC运行时确定类层次的关键,它一个简单的单向链表

    // Operations
    CObject* CreateObject(); //这个函数给予CObject 派生类运行时动态建立的能力
    BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

    //这个函数使用 m_pBaseClass或 m_pfnGetBaseClass遍历整个类层次确定是否pBaseClass指向的类是基类,
    //使用它可以判断某类是否是从pBaseClass指向的类在派生来。

    // Implementation
    void Store(CArchive& ar) const;
    static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

    // CRuntimeClass objects linked together in simple list
    CRuntimeClass* m_pNextClass; // linked list of registered classes
    };



    二、CObject类
    CObject是MFC类的大多数类的基类,主要是通过它实现:
    (1)、运行类信息;(2)、序列化;(3)、对象诊断输出;(4)、同集合类相兼容;
    (1)、运行时类信息:
    注 意:要想使用CRuntimeClass结构得到运行时类信息,你必须在你的类中包括 DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC、 DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE或DECLARE_SERIAL/IMPLEMENT_SERIAL。但你 的类必须是从CObject派生的才能使用这些宏, 因为通过DECLARE_DYNAMIC将定义一个实现如下的函数:
    CRuntimeClass* PASCAL B::_GetBaseClass()
    {
    return RUNTIME_CLASS(base_name);
    }

    其中的RUNTIME_CLASS是这样定义的
    #define RUNTIME_CLASS( class_name )
    (CRuntimeClass *)(&class_name:
    展开全文
  • bat批处理文字字符串替换

    千次阅读 2019-03-09 18:25:36
    cd /d %~dp0 REM 执行对应的出包命令 传递自己对应产品的参数 start /min "compress" run_script.bat "soc" rem 等待200秒 待编译完成后进行字符串替换处理 @choice /t 200 /d y /n >nul rem 关闭打包窗口 taskkill ...

    1、背景

    项目中使用一些字体是通过外网请求获取的(项目规定不允许跨域请求),这部分代码在node_modules依赖中,且无法通过样式覆盖解决。因此需要在编译好的代码中进行群替换。

    2、解决方案

    1)使用bat进行编译管理新建run_script.bat 

    @echo off
    REM 声明采用UTF-8编码
    chcp 65001
    REM 当前目录 执行编译命令 %1%接收的第一个参数
    cmd /c npm run-script build:%1%
    

    2)新建security_check.vbs处理字符串替换

    rem 获取传进来的参数
    Set objArgs = WScript.Arguments
    rem msgbox objArgs(0)
    rem 接收传捡来的参数
    url = objArgs(0)
    rem 接收要替换的字符串
    str = objArgs(1)
    rem 定义文件对象
    Set fileObj = Wscript.CreateObject("Scripting.FileSystemObject")
    rem 打开文件
    Set files = fileObj.opentextfile(url)
    rem 替换css字符串
    css = replace(files.ReadAll, str, "assets/font")
    rem 文件关闭
    files.close
    rem 替换后的文件写入
    Set r = fileObj.opentextfile(url,2,true)
    r.write css
    Wscript.quit
    

    3)新建chubao.bat 调用run_script.bat 执行编译命令后再调用security_check.vbs进行字符串替换(特殊字符会有问题,建议使用python处理)

    @echo off
    REM 声明采用UTF-8编码
    chcp 65001
    REM 今日当前目录
    cd /d %~dp0
    REM 执行对应的出包命令 传递自己对应产品的参数
    start /min "compress" run_script.bat "soc"
    rem 等待200秒 待编译完成后进行字符串替换处理
    @choice /t 200 /d y /n >nul
    rem 关闭打包窗口
    taskkill /f /fi "WINDOWTITLE eq compress"
    REM 进入到打完的包路径下
    cd /d dist
    rem 循环遍历获取css文件名
    for /f "delims=" %%A in ('dir /b *.css') do set "name=%%A"
    rem 判断是否有外网连接 如果有则进行字符串替换
    findstr https://at.alicdn.com/t %name%>nul && (
    		echo 存在外网请求 开始处理
    		cd ..
    		rem vsb处理字符串
    		WScript.exe security_check.vbs dist/%name% "https://at.alicdn.com/t"
    		rem phthon处理字符串
    		rem python security_check.py 	
    )||echo 不存在外网请求
    rem 打印成功消息
    echo 编译成功
    cmd.exe
    

    4)有python环境的也可以使用python进行字符串替换(建议使用python处理)

    #-*-coding:utf-8-*- 
    
    import os
    filepath = "./dist/"
    files = os.listdir(filepath)
    
    for file in files:
        if ".css" in file :
            with open(filepath+file,'r+', encoding='UTF-8') as f:
                text = f.read()
                f.seek(0)
                f.truncate()
                text = str(text).replace("https://at.alicdn.com/t","assets/font")
                f.write(text)
                f.close()
    				

    备注:win10默认不支持vbs需要对注册表进行修改(复制粘贴到文件中,后缀给为.reg)双击即可

    Windows Registry Editor Version 5.00
    [HKEY_CLASSES_ROOT\.VBS]
    @="VBSFile"
    [HKEY_CLASSES_ROOT\VBSFile]
    @="VBScript File"
    [HKEY_CLASSES_ROOT\VBSFile\ScriptEngine]
    @="VBScript"
    [HKEY_CLASSES_ROOT\VBSFile\ScriptHostEncode]
    @="{85131631-480C-11D2-B1F9-00C04F86C324}"
    [HKEY_CLASSES_ROOT\VBSFile\Shell]
    @=""
    [HKEY_CLASSES_ROOT\VBSFile\Shell\Open]
    @="打开(&O)"
    [HKEY_CLASSES_ROOT\VBSFile\Shell\Open\Command]
    @=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
    00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,57,00,53,00,\
    63,00,72,00,69,00,70,00,74,00,2e,00,65,00,78,00,65,00,20,00,22,00,25,00,31,\
    00,22,00,20,00,25,00,2a,00,00,00
    [HKEY_CLASSES_ROOT\VBSFile\ShellEx]
    [HKEY_CLASSES_ROOT\VBSFile\ShellEx\DropHandler]
    @="{60254CA5-953B-11CF-8C96-00AA00B8708C}"
    [HKEY_CLASSES_ROOT\VBSFile\ShellEx\PropertySheetHandlers]
    [HKEY_CLASSES_ROOT\VBSFile\ShellEx\PropertySheetHandlers\WSHProps]
    @="{60254CA5-953B-11CF-8C96-00AA00B8708C}"

    展开全文
  • httpclient 请求返回字符串

    千次阅读 2017-06-02 16:27:13
    // 输出完成后刷新并关闭流 dataout.flush(); dataout.close(); // 重要且易忽略步骤 (关闭流,切记!) // 连接发起请求,处理服务器响应 (从连接获取到输入流并包装为bufferedReader) BufferedReader bf =...
  • Delphi字符串函数大全

    千次阅读 2005-03-18 03:53:00
    字符串函数大全】 首部 function AnsiResemblesText(const AText, AOther: string): Boolean; $[StrUtils.pas 功能 返回两个字符串是否相似 说明 ANSI(American National Standards Institute)美国国家标准协会;
  • C语言实战(一)——文本字符串替换

    千次阅读 多人点赞 2018-06-07 18:26:08
    前言: 昨天在微信公众号看到一篇博文,讲的是在linux环境中利用C语言将一个文本里的内容读出来,把里面的某个字符串替换成另一个字符串,再将内容回写回文本里。这个需求很简单,使用更高级的语言,比如Python,...
  • 暴雪公司关于字符串匹配的hash算法

    万次阅读 2012-07-11 14:54:51
    暴雪公司有个经典的字符串的hash公式  先提一个简单的问题,假如有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做? 有一个方法最简单,老老实实...
  • MSDN中查阅VARIANT类型,可以用BSTR表示字符串,但所有的BSTR都包含宽字符,而只有Windows NT支持宽字符,Windows 9X并不支持。所以要完成一个适应各平台的串口应用程序必须解决这个问题,这里使用CByteArray解决之...
  • Notepad++绝对是windows下进行程序编辑的神器之一,要更快速的使用以媲美VIM,必须灵活掌握它的快捷键,下面对notepad++默认的快捷键做个整理(其中有颜色的为常用招数): 1. 文件相关 快捷键 ...
  • C语言模仿post请求并解析返回的json字符串 #include &amp;amp;amp;amp;lt;stdio.h&amp;amp;amp;amp;gt; #include &amp;amp;amp;amp;lt;string.h&amp;amp;amp;amp;gt; #include &amp;amp;amp;amp...
  • 常量字符串存储与销毁引发的误解

    千次阅读 2011-03-06 03:04:00
    这边文章主要讨论常量字符串存储与销毁引发的误解,问题出现的本质原因在于编译器对堆、栈和静态存储区的使用策略,以及VS开发环境对Debug版本和Release版本的处理方式不同。
  • CString 用法总结(字符串转换)

    千次阅读 2010-05-16 23:25:00
    CString 操作指南原著:Joseph M. Newcomer翻译:littleloach原文出处:codeproject...它们很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作的时候方便了很多。不管怎样,使用CString有很多特殊的技巧,特别是
  • //从pos开始从后向前查找字符串s中前n个字符组成的字符串在当前串中的位置,成功返回所在位置,失败时返回string::npos的值  int find_first_of(char c, int pos = 0) const;//从pos开始查找字符c第一次出现的...
  • _T的意思是通知编译器,自行进行字符串的多字节/Unicode转换。 而L表示,该字符串为Unicode版本。 http://www.blogjava.net/neumqp/archive/2006/03/09/34504.html http://blog.csdn.net/sl159/articl
  • Windows串口API编程

    千次阅读 2014-10-28 13:30:47
    (一)Windows API串口通信编程概述 Windows环境下的串口编程与DOS环境下的串口编程有很大不同。Windows环境下的编程的最大特征之一就是设备无关性,它通过设备驱动程序将Windows应用程序同不同的外部设备隔离。...
  • 参考:http://lumingfeng.xyz 想在windows下使用熟悉一下redis,首先在它的github上下载压缩包:https://github.com/dmajkic/redis 下载到本地之后解压缩打开,里面有32位和64位2个文件夹,我的是64位系统,所以把...
  • windows下C++读取串口数据

    万次阅读 2018-10-15 10:08:17
    windows下C++读取串口数据 这里提供一个类: SerialPort.h #pragma once #ifndef SERIALPORT_H_ #define SERIALPORT_H_ #include &lt;Windows.h&gt; #include"DataFrame.h" /** ...
  • VB查找并替换WORD文档中的字符串

    千次阅读 2008-12-16 09:37:00
    MsgBox "已完成对文档的搜索并完成 " & I & " 替换。" '提示总替换次数 '如果替换成功,则提示是否保存 If I > 0 Then '判断是否需要另存 If Trim(SaveFile) <> "" Then '需要另存 If Dir(SaveFile) = "" ...
  • windows的api进行rs232串口通信

    千次阅读 2014-03-20 09:45:23
    Windows环境下,C++的开发工具既没有提供象DOS和BIOS中那样专门的串行通讯控制方法,也不允许用户直接控制串口的中断。 为了保证资源共享,Windows系统完全接管了各种硬件资源,使用中断来控制端口将破坏系统的多...
  • 本章将介绍Windows Embedded CE 7.0的串口通信。一些基于Windows CE的设备可以与其他计算机、打印机、调制解调器,或全球定位系统(GPS)卫星通信由串行连接方式。 串行I / O是Windows CE支持的最简单的通信...
  • Windows基于串口API函数的串口程序开发介绍欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格...
  • VC串口通信(Windows API)

    万次阅读 多人点赞 2018-08-17 16:17:47
    本文转载自基于Windows API的VC++串口通信详解,以下附上大白的理解。 简介 在Win32下,可以使用两种编程方式实现串口通信,其一是使用ActiveX控件,这种方法程序简单,但欠灵活。其二是调用Windows的API函数,...
  • 我们可以在【所有】这个条目中看见连接的成功后的信息,这些也就是open函数中填写的连接字符串了。 7、最后一步 我们把udl文件关闭,将文件名后缀改为txt。再打开,后面两行就是你的连接信息了,copy...
  • 9.1 设定逾时参数(time-out parameters) 应用程序每次打开一个通信端口时,必须使用COMMTIMEOUTS结构设置通信超时。如果这个结构未被配置,端口使用由驱动程序...当读写操作超时,操作完成,而且ReadFile和Write
  • 一 : 关于Windows的默认共享介绍 网上其实到处都有谈论到,现我也只是整理一下: 在在Windows 系统中,在“我的电脑”上右击“管理”,依次选择“系统工具→共享文件夹→共享”,就会看到一些带有美元“$”标记的...
  • Windows串口通信相关API函数

    万次阅读 2011-07-29 10:19:53
    “工欲善其事,必先利其器”,这一节将从使用的角度出发,对和串口通信相关的32位的Windows API函数进行介绍,力图使你们对其有个全面、准确的认识。 2.1 打开和关闭串口1. 打开串口在32位的Windows系统中,串口...
  • Alt+Shift+L 抽取本地变量(可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候) Alt+Shift+F把Class中的local变量变为field变量 (比较实用的功能) Alt+Shift+I合并变量(可能这样说有点不妥...
  • windows7问题集合

    千次阅读 2009-09-18 13:01:00
    windows7的任务栏不能添加快捷方式图标?】 解决方法:原因就是去除了快捷图标的小箭头的缘故,所以只需要将快捷方式的小箭头恢复就正常了!...在lnkfile子项中添加IsShortcut字符串值项,因为IsSh
  • Visual Studio 2010的新特性里面Ribbon界面...为了让我们的应用程序“Ready for Windows 7”,我们有必要根据实际的情况需要,为我们的应用程序添加Ribbon界面。 为了推广Ribbon界面,为了讨好程序员们,让程序员可以

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 130,284
精华内容 52,113
关键字:

windows7关闭完成字符串