VC常用数据类型使用转换详解

读者层次:初学
刚接触VC编程的朋友往往对许多数据类型的转换感到迷惑不解,本文将介绍一些常用数据类型的使用。
我们先定义一些常见类型变量借以说明

1
2
3
4
5
6
7
8
9
10
int i = 100;
long l = 2001;
float f=300.2;
double d=12345.119;
char username[]="程佩君";
char temp[200];
char *buf;
CString str;
_variant_t v1;
_bstr_t v2;

一、其它数据类型转换为字符串

短整型(int)

1
2
itoa(i,temp,10);///将i转换为字符串放入temp中,最后一个数字表示十进制
itoa(i,temp,2); ///按二进制方式转换

长整型(long)

1
ltoa(l,temp,10);

浮点数(float,double)
用fcvt可以完成转换,这是MSDN中的例子:

1
2
3
4
int decimal, sign;
char *buffer;
double source = 3.1415926535;
buffer = _fcvt( source, 7, &decimal, &sign );

运行结果:source: 3.1415926535 buffer: ‘31415927’ decimal: 1 sign: 0
decimal表示小数点的位置,sign表示符号:0为正数,1为负数
CString变量

1
2
str = "2008北京奥运";
buf = (LPSTR)(LPCTSTR)str;

BSTR变量

1
2
3
4
5
BSTR bstrValue = ::SysAllocString(L"程序员");
char * buf = _com_util::ConvertBSTRToString(bstrValue);
SysFreeString(bstrValue);
AfxMessageBox(buf);
delete(buf);

CComBSTR变量

1
2
3
4
CComBSTR bstrVar("test");
char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str);
AfxMessageBox(buf);
delete(buf);

_bstr_t变量
_bstr_t类型是对BSTR的封装,因为已经重载了=操作符,所以很容易使用

1
2
3
_bstr_t bstrVar("test");
const char *buf = bstrVar;///不要修改buf中的内容
AfxMessageBox(buf);

通用方法(针对非COM数据类型)
用sprintf完成转换

1
2
3
4
5
6
7
8
9
char  buffer[200];
char c = '1';
int i = 35;
long j = 1000;
float f = 1.7320534f;
sprintf( buffer, "%c",c);
sprintf( buffer, "%d",i);
sprintf( buffer, "%d",j);
sprintf( buffer, "%f",f);

二、字符串转换为其它数据类型

1
strcpy(temp,"123");

短整型(int)

1
i = atoi(temp);

长整型(long)

1
l = atol(temp);

浮点(double)

1
d = atof(temp);

CString变量

1
CString name = temp;

BSTR变量

1
2
3
BSTR bstrValue = ::SysAllocString(L"程序员");
...///完成对bstrValue的使用
SysFreeString(bstrValue);

CComBSTR变量
CComBSTR类型变量可以直接赋值

1
2
CComBSTR bstrVar1("test");
CComBSTR bstrVar2(temp);

_bstr_t变量
_bstr_t类型的变量可以直接赋值

1
2
_bstr_t bstrVar1("test");
_bstr_t bstrVar2(temp);

三、其它数据类型转换到CString
使用CString的成员函数Format来转换,例如:

整数(int)

1
str.Format("%d",i);

浮点数(float)

1
str.Format("%f",i);

字符串指针(char *)等已经被CString构造函数支持的数据类型可以直接赋值

1
str = username;

对于Format所不支持的数据类型,可以通过上面所说的关于其它数据类型转化到char 的方法先转到char ,然后赋值给CString变量。

四、BSTR、_bstr_t与CComBSTR

CComBSTR 是ATL对BSTR的封装,_bstr_t是C++对BSTR的封装,BSTR是32位指针,但并不直接指向字串的缓冲区。
char *转换到BSTR可以这样:

1
2
BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上comutil.h和comsupp.lib
SysFreeString(bstrValue);

反之可以使用

1
2
char *p=_com_util::ConvertBSTRToString(b);
delete p;

具体可以参考一,二段落里的具体说明。

CComBSTR与_bstr_t对大量的操作符进行了重载,可以直接进行=,!=,==等操作,所以使用非常方便。
特别是_bstr_t,建议大家使用它。

五、VARIANT 、_variant_t 与 COleVariant

VARIANT的结构可以参考头文件VC98IncludeOAIDL.H中关于结构体tagVARIANT的定义。
对于VARIANT变量的赋值:首先给vt成员赋值,指明数据类型,再对联合结构中相同数据类型的变量赋值,举个例子:

1
2
3
4
VARIANT va;
int a=2001;
va.vt=VT_I4;///指明整型数据
va.lVal=a; ///赋值

对于不马上赋值的VARIANT,最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为VT_EMPTY,下表我们列举vt与常用数据的对应关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Byte bVal;  // VT_UI1.
Short iVal; // VT_I2.
long lVal; // VT_I4.
float fltVal; // VT_R4.
double dblVal; // VT_R8.
VARIANT_BOOL boolVal; // VT_BOOL.
SCODE scode; // VT_ERROR.
CY cyVal; // VT_CY.
DATE date; // VT_DATE.
BSTR bstrVal; // VT_BSTR.
DECIMAL FAR* pdecVal // VT_BYREF|VT_DECIMAL.
IUnknown FAR* punkVal; // VT_UNKNOWN.
IDispatch FAR* pdispVal; // VT_DISPATCH.
SAFEARRAY FAR* parray; // VT_ARRAY|*.
Byte FAR* pbVal; // VT_BYREF|VT_UI1.
short FAR* piVal; // VT_BYREF|VT_I2.
long FAR* plVal; // VT_BYREF|VT_I4.
float FAR* pfltVal; // VT_BYREF|VT_R4.
double FAR* pdblVal; // VT_BYREF|VT_R8.
VARIANT_BOOL FAR* pboolVal; // VT_BYREF|VT_BOOL.
SCODE FAR* pscode; // VT_BYREF|VT_ERROR.
CY FAR* pcyVal; // VT_BYREF|VT_CY.
DATE FAR* pdate; // VT_BYREF|VT_DATE.
BSTR FAR* pbstrVal; // VT_BYREF|VT_BSTR.
IUnknown FAR* FAR* ppunkVal; // VT_BYREF|VT_UNKNOWN.
IDispatch FAR* FAR* ppdispVal; // VT_BYREF|VT_DISPATCH.
SAFEARRAY FAR* FAR* pparray; // VT_ARRAY|*.
VARIANT FAR* pvarVal; // VT_BYREF|VT_VARIANT.
void FAR* byref; // Generic ByRef.
char cVal; // VT_I1.
unsigned short uiVal; // VT_UI2.
unsigned long ulVal; // VT_UI4.
int intVal; // VT_INT.
unsigned int uintVal; // VT_UINT.
char FAR * pcVal; // VT_BYREF|VT_I1.
unsigned short FAR * puiVal; // VT_BYREF|VT_UI2.
unsigned long FAR * pulVal; // VT_BYREF|VT_UI4.
int FAR * pintVal; // VT_BYREF|VT_INT.
unsigned int FAR * puintVal; //VT_BYREF|VT_UINT.

_variant_t是VARIANT的封装类,其赋值可以使用强制类型转换,其构造函数会自动处理这些数据类型。
使用时需加上#include <comdef.h>
例如:

1
2
3
4
5
6
7
8
9
long l=222;
ing i=100;
_variant_t lVal(l);
lVal = (long)i;

COleVariant的使用与_variant_t的方法基本一样,请参考如下例子:
COleVariant v3 = "字符串", v4 = (long)1999;
CString str =(BSTR)v3.pbstrVal;
long i = v4.lVal;

六、其它一些COM数据类型

根据ProgID得到CLSID

1
2
3
HRESULT CLSIDFromProgID( LPCOLESTR lpszProgID,LPCLSID pclsid);
CLSID clsid;
CLSIDFromProgID( L"MAPI.Folder",&clsid);

根据CLSID得到ProgID
WINOLEAPI ProgIDFromCLSID( REFCLSID clsid,LPOLESTR * lplpszProgID);
例如我们已经定义了 CLSID_IApplication,下面的代码得到ProgID

1
2
3
4
LPOLESTR pProgID = 0;
ProgIDFromCLSID( CLSID_IApplication,&pProgID);
...///可以使用pProgID
CoTaskMemFree(pProgID);//不要忘记释放

七、ANSI与Unicode
Unicode称为宽字符型字串,COM里使用的都是Unicode字符串。

将ANSI转换到Unicode
(1)通过L这个宏来实现,例如: CLSIDFromProgID( L”MAPI.Folder”,&clsid);
(2)通过MultiByteToWideChar函数实现转换,例如:

1
2
3
4
5
char *szProgID = "MAPI.Folder";
WCHAR szWideProgID[128];
CLSID clsid;
long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID));
szWideProgID[lLen] = '';

(3)通过A2W宏来实现,例如:

1
2
USES_CONVERSION;
CLSIDFromProgID( A2W(szProgID),&clsid);

将Unicode转换到ANSI
(1)使用WideCharToMultiByte,例如:

1
2
3
// 假设已经有了一个Unicode 串 wszSomeString...
char szANSIString [MAX_PATH];
WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL );

(2)使用W2A宏来实现,例如:

1
2
USES_CONVERSION;
pTemp=W2A(wszSomeString);

八、其它

对消息的处理中我们经常需要将WPARAM或LPARAM等32位数据(DWORD)分解成两个16位数据(WORD),例如:

1
2
3
LPARAM lParam;
WORD loValue = LOWORD(lParam);///取低16位
WORD hiValue = HIWORD(lParam);///取高16位

对于16位的数据(WORD)我们可以用同样的方法分解成高低两个8位数据(BYTE),例如:

1
2
3
WORD wValue;
BYTE loValue = LOBYTE(wValue);///取低8位
BYTE hiValue = HIBYTE(wValue);///取高8位

两个16位数据(WORD)合成32位数据(DWORD,LRESULT,LPARAM,或WPARAM)

1
2
3
4
LONG MAKELONG( WORD wLow, WORD wHigh );
WPARAM MAKEWPARAM( WORD wLow, WORD wHigh );
LPARAM MAKELPARAM( WORD wLow, WORD wHigh );
LRESULT MAKELRESULT( WORD wLow, WORD wHigh );

两个8位的数据(BYTE)合成16位的数据(WORD)

1
WORD MAKEWORD( BYTE bLow, BYTE bHigh );

从R(red),G(green),B(blue)三色得到COLORREF类型的颜色值

1
COLORREF RGB( BYTE byRed,BYTE byGreen,BYTE byBlue );

例如COLORREF bkcolor = RGB(0x22,0x98,0x34);

从COLORREF类型的颜色值得到RGB三个颜色值

1
2
3
BYTE Red = GetRValue(bkcolor); ///得到红颜色
BYTE Green = GetGValue(bkcolor); ///得到绿颜色
BYTE Blue = GetBValue(bkcolor); ///得到兰颜色

九、注意事项
假如需要使用到ConvertBSTRToString此类函数,需要加上头文件comutil.h,并在setting中加入comsupp.lib或者直接加上#pragma comment( lib, “comsupp.lib” )

Compartir Comentarios

使用敏捷开发解决实数转中文大写金额问题

实数转中文大写的问题,虽然不能算是太难,但却也不是那种能一气呵成,一蹴而就的简单问题,一步到位的想法很容易就会陷入泥潭;正确的做法应该 是对转换的规律抽丝剥茧,由浅入深一步一步完成转换步骤,如此便能水到渠成……敏捷Programming的思想很适用于解决此类问题,借此机会正好和大家分享一些敏捷Programming 的经验。

开始之前,先看一下大写位进换数情况先,这里以目前财务体系的中法换算为准:

个=10的0次方
十=10的1次方
百=10的2次方
千=10的3次方
万=10的4次方
亿=10的8次方
兆=10的12次方
京=10的16次方
垓=10的20次方
杼=10的24次方
穰=10的28次方
沟=10的32次方
涧=10的36次方
正=10的40次方
载=10的44次方
极=10的48次方
恒河砂=10的52次方
阿僧祇=10的56次方
不可思议=10的60次方
中法换算中,“个十百千”可认为是基本单位,而从开始会复用这些基本单位,用完这4个基本单位之后就会有新的单位出现。然后再利用这4个基本单位,如此循环往复。因此,首要解决的就是“个十百千”这个级别上的转换问题,一则是因为它最简单,二则是它的转换逻辑还可以为以后更高层次的转换所复用。

先考虑最简单的情形,即不带零和小数的情况:

1
2
3
4
5
6
[Test]
public void SimpleConvert()
{
CurrencyConvert convert = new CurrencyConvert(1234M);
Assert.AreEqual(“壹仟贰佰叁拾肆元整”, convert.ToString());
}

没有小数,也先不考虑零的情况。在我们编写类来通过测试之前,先分析一下大写转换规律:

上表中,第三行,白底部分我们称为数字,灰底部分称为位字,红色的箭头代表我们分析金额时的顺序,是从右至左,第一个数字的位字为,此位字不作输出;第二个数字的位字为;第三个为;第四个为。那么,只要从右向左分别得出数字及其位字,依次插入到列表的头,再串联起来就可以得到其大写形式了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CurrencyConvert
{
private static string[] symbols1 = new string[] { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
private static string[] symbols2 = new string[] { "", "拾", "佰", "仟" };
private decimal _source;
public CurrencyConvert(decimal source)
{
_source = source;
}
public override string ToString()
{
string sourceString = _source.ToString("F");
List<string> list = new List<string>();
for (int i = sourceString.Length - 1, j = 0; i >= 0; i--, j++)
{
list.Insert(0, symbols2[j]);
list.Insert(0, symbols1[int.Parse(sourceString[i].ToString())]);
}
return string.Join("", list.ToArray()) + "元整";
}
}

现在考虑带零的情况,零的转换有以下几种情况:

  1. 零后面不打印位字(如1024为“壹仟零贰拾肆元整”)
  2. 当有多个零连续出现的时候只打印一个零(如1004为“壹仟零肆元整”)
  3. 连续的零一直延伸到个位的时候不打印零(如1200为“壹仟贰佰元整”)
    针对这带零的情况编写单位测试:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [Test]
    public void SimpleWithZeroConvert()
    {
    CurrencyConvert convert1 = new CurrencyConvert(1024M);
    Assert.AreEqual("壹仟零贰拾肆元整", convert1.ToString());
    CurrencyConvert convert2 = new CurrencyConvert(1004M);
    Assert.AreEqual("壹仟零肆元整", convert2.ToString());
    CurrencyConvert convert3 = new CurrencyConvert(1200M);
    Assert.AreEqual("壹仟贰佰元整", convert3.ToString());
    }

情况一容易解决,只要稍加判断当前数字是否为零即可;第二个也不难,只要判断最后一个插入的是不是零即可,但是在进行这个判断之前要先判断列表 是否为空,否则取值时会引发异常;第三个情况,看起来相对复杂一点,但其实在第二点判断列表是否为空的时候这个问题也同时被解决了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class CurrencyConvert
{
private static string[] symbols1 = new string[] { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
private static string[] symbols2 = new string[] { "", "拾", "佰", "仟" };
private decimal _source;
public CurrencyConvert(decimal source)
{
_source = source;
}
public override string ToString()
{
string sourceString = _source.ToString("F");
List<string> list = new List<string>();
for (int i = sourceString.Length - 1, j = 0; i >= 0; i--, j++)
{
int number = int.Parse(sourceString[i].ToString());
if (number == 0)
{
if (list.Count > 0 && list[0] != symbols1[0])
list.Insert(0, symbols1[number]);
}
else
{
list.Insert(0, symbols2[j]);
list.Insert(0, symbols1[number]);
}
}
return string.Join("", list.ToArray()) + "元整";
}
}

好,接下来,在向更高位迈进之前,先来解决一下小数的问题。相对来讲,小数的问题比较容易处理,我可不想在最困难的部分解决之后还要来料理这些细节。小数部分,大概需要考虑以下几种情形:

  1. 如果被转换数小于1,那么不打印“整数部分”的信息,直接打印小数部分且前面不打印“零”
  2. 如果被转换数大于1,则先打印“整数部分”,再打印“小数部分”,中间有多个零时只打印一个零
  3. 如果不存在小数部分,则返回“整”(这个在前面整数部分的测试用例已被覆盖)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [Test]
    public void DecimalConvert()
    {
    CurrencyConvert convert1 = new CurrencyConvert(0.1234567M);
    Assert.AreEqual("壹角贰分叁厘肆毫伍丝陆忽", convert1.ToString());
    CurrencyConvert convert2 = new CurrencyConvert(0.001M);
    Assert.AreEqual("壹厘", convert2.ToString());
    CurrencyConvert convert3 = new CurrencyConvert(0.01M);
    Assert.AreEqual("壹分", convert3.ToString());
    CurrencyConvert convert4 = new CurrencyConvert(1.1M);
    Assert.AreEqual("壹元壹角", convert4.ToString());
    CurrencyConvert convert5 = new CurrencyConvert(1.001M);
    Assert.AreEqual("壹元零壹厘",convert5.ToString());
    }

如上所示,小数部分,我们假定精确到“忽”,余下部分就忽略掉。如下图所示,小数部分的拼装顺序和整数部分的拼装顺序略有不同。这时,应该对程序进行重构,把总的拼装逻辑分为整数部分小数部分

重构后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class CurrencyConvert
{
private static string[] symbols0 = new string[] { "角", "分", "厘", "毫", "丝", "忽" };
private static string[] symbols1 = new string[] { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
private static string[] symbols2 = new string[] { "", "拾", "佰", "仟" };
private decimal _source;
public CurrencyConvert(decimal source)
{
_source = source;
}
public override string ToString()
{
string[] ary = _source.ToString("F").Split('.');
return ConvertIntegerPart(ary[0]) + ConvertDecimalPart(ary.Length == 2 ? ary[1] : string.Empty);
}
private string ConvertIntegerPart(string integerPart)
{
if (string.IsNullOrEmpty(integerPart) || decimal.Parse(integerPart) == 0M)
return string.Empty;
List<string> list = new List<string>();
for (int i = integerPart.Length - 1, j = 0; i >= 0; i--, j++)
{
int number = int.Parse(integerPart[i].ToString());
if (number == 0)
{
if (list.Count > 0 && list[0] != symbols1[0])
list.Insert(0, symbols1[number]);
}
else
{
list.Insert(0, symbols2[j]);
list.Insert(0, symbols1[number]);
}
}
if (list.Count > 0) list.Add("元");
return string.Join(string.Empty, list.ToArray());
}
private string ConvertDecimalPart(string decimalPart)
{
if (string.IsNullOrEmpty(decimalPart) || decimal.Parse(decimalPart) == 0M)
return "整";
List<string> list = new List<string>();
for (int i = 0; i < Math.Min(decimalPart.Length, symbols0.Length); i++)
{
int number = int.Parse(decimalPart[i].ToString());
if (number == 0)
{
if ((list.Count == 0 && _source > 1M) || (list.Count > 0 && list[list.Count - 1] != symbols1[0]))
list.Add(symbols1[number]);
}
else
{
list.Add(symbols1[number]);
list.Add(symbols0[i]);
}
}
return string.Join(string.Empty, list.ToArray());
}
}

完成了这个部份之后,我突然想到,如果传进去的是 0 ,则程序会正常输出“零元”吗?,所以我写多了一个测试用例:

1
2
3
4
5
6
[Test]
public void ZeroConvert()
{
CurrencyConvert convert = new CurrencyConvert(0M);
Assert.AreEqual("零元", convert.ToString());
}

这是一个十分特殊的的case,这时候左右部分都为空,而且原来的拼装法则也用上不了,所以我把它放在了总拼装函数ToString之前作一个特殊化判断。

1
2
3
4
5
6
public override string ToString()
{
if (_source == decimal.Zero) return "零元";
string[] ary = _source.ToString("F").Split('.');
return ConvertIntegerPart(ary[0]) + ConvertDecimalPart(ary.Length == 2 ? ary[1] : string.Empty);
}

好了,相对简单的部份解决了。现在来研究“万元”以上的情况:

再往左的情况就是红色部份会被下一个单位替换,这种简单重复而已。最右边空的其实就是个位,由此我们可以总结出拼装的规律:

  1. 数字每4个一组,从右至左使用“个万亿兆京……”为单位
    需要再重构一次,把原来的整数转换逻辑提取为组的转换ConvertGroup
  2. 每组数字从右至左使用“个十百千”为单位串联
    原来的整数转换逻辑
  3. 当某一组和其右边所有组全为零时,这些组将不被打印
    ConvertGroup的结果稍加判断即可实现
    依旧先从简单开始,编写第一点的测试用列:
    1
    2
    3
    4
    5
    6
    [Test]
    public void TenThousandAboveConvert()
    {
    CurrencyConvert convert1 = new CurrencyConvert(123456789012M);
    Assert.AreEqual("壹仟贰佰叁拾肆亿伍仟陆佰柒拾捌万玖仟零壹拾贰元整", convert1.ToString());
    }

接下来是实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private string ConvertIntegerPart(string integerPart)
{
if (string.IsNullOrEmpty(integerPart) || decimal.Parse(integerPart) == 0M)
return string.Empty;
List<string> list = new List<string>();
// 开始新的转换逻辑
const int groupMaxLength = 4;
int residue = integerPart.Length % groupMaxLength;
int groupsCount = integerPart.Length / groupMaxLength + (residue > 0 ? 1 : 0);
int lastGroupCount = residue == 0 ? groupMaxLength : residue;
for (int i = groupsCount - 1, j = 0; i >= 0 && j < symbols3.Length ; i--, j++)
{
int groupStart = i == 0 ? 0 : lastGroupCount + (i - 1) * groupMaxLength;
int groupLength = i == 0 ? lastGroupCount : groupMaxLength;
list.Insert(0, symbols3[j]);
list.Insert(0, ConvertGroup(integerPart.Substring(groupStart, groupLength)));
}
// 完成新的转换逻辑
if (list.Count > 0) list.Add("元");
return string.Join(string.Empty, list.ToArray());
}
// 原先的简单转换逻辑被提取出来
private string ConvertGroup(string group)
{
List<string> list = new List<string>();
for (int i = group.Length - 1, j = 0; i >= 0; i--, j++)
{
int number = int.Parse(group[i].ToString());
if (number == 0)
{
if (list.Count > 0 && list[0] != symbols1[0])
list.Insert(0, symbols1[number]);
}
else
{
list.Insert(0, symbols2[j]);
list.Insert(0, symbols1[number]);
}
}
return string.Join(string.Empty, list.ToArray());
}

组的分割还挺不容易,调试了许久,可惜看起来还是不太优雅,暂时就先这样吧,起码它已经能正确运行。好,现在加上零的情况:

1
2
3
4
5
6
7
8
[Test]
public void TenThousandAboveConvert()
{
CurrencyConvert convert1 = new CurrencyConvert(123456789012M);
Assert.AreEqual("壹仟贰佰叁拾肆亿伍仟陆佰柒拾捌万玖仟零壹拾贰元整", convert1.ToString());
CurrencyConvert convert2 = new CurrencyConvert(12300M);
Assert.AreEqual("壹万贰仟叁佰元整", convert2.ToString());
}

支持这个真是相当简单,只要对组的拼装结果进行判断就行了

1
2
3
4
5
6
7
8
9
10
11
for (int i = groupsCount - 1, j = 0; i >= 0 && j < symbols3.Length ; i--, j++)
{
int groupStart = i == 0 ? 0 : lastGroupCount + (i - 1) * groupMaxLength;
int groupLength = i == 0 ? lastGroupCount : groupMaxLength;
string groupString = ConvertGroup(integerPart.Substring(groupStart, groupLength));
if (!string.IsNullOrEmpty(groupString))
{
list.Insert(0, symbols3[j]);
list.Insert(0, groupString);
}
}

最后,综合测试:

1
2
3
4
5
6
[Test]
public void FinnalTest()
{
CurrencyConvert convert = new CurrencyConvert(120004000001.001M);
Assert.AreEqual("壹仟贰佰亿零肆佰万零壹元零壹厘", convert.ToString());
}

测试用例是我们假想出来程序可能遇到的情况,通常这些用例都是有代表性的,但往往不能覆盖每种情形,因此,完成单元测试后,我做了一个简单的winform来做烟雾测试,确定程序能正常运行。

Compartir Comentarios

理解Javascript的闭包

先来看两段代码,若是你不能完全理解它们的原理,则本文对你还是有一点参考作用的。

首先是我写的一段用来模拟私有成员的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Foobar(parameter) {
var privateVariable = "I'm private Variable";
var privateFunction = function() {
return "I'm privateFunction and privateVariable is : " + privateVariable;
}

this.publicVariable = "I'm public Variable";
this.publicFunction = function() {
document.write("parameter : " + parameter + "<br />");
document.write("privateVariable : " + privateVariable + "<br />");
document.write("privateFunction : " + privateFunction() + "<br />");
document.write("publicVariable : " + this.publicVariable + "<br />");
}
}
var foobar = new Foobar("I'm paramter");
foobar.publicFunction();
document.write("typeof(foobar.privateVariable) : " + typeof(foobar.privateVariable) + "<br />");
document.write("typeof(foobar.privateFunction) : " + typeof(foobar.privateFunction) + "<br />");
document.write("typeof(foobar.publicVariable) : " + typeof(foobar.publicVariable) + "<br />");
document.write("typeof(foobar.publicFunction) : " + typeof(foobar.publicFunction) + "<br />");

输出内容:

1
2
3
4
5
6
7
8
parameter : I'm paramter
privateVariable : I'm private Variable
privateFunction : I'm privateFunction and privateVariable is : I'm private Variable
publicVariable : I'm public Variable
typeof(foobar.privateVariable) : undefined
typeof(foobar.privateFunction) : undefined
typeof(foobar.publicVariable) : string
typeof(foobar.publicFunction) : function

可以看到,privateVariable和privateFunction都被成员的隐藏了起来。再看一段来自dojo类库的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var getImgInPositionedDivHtml = (function(){
var buffAr = [
'<div id=”‘,
'', //index 1, DIV ID attribute
'“ style=”position:absolute;top:',
'', //index 3, DIV top position
'px;left:',
'', //index 5, DIV left position
'px;width:',
'', //index 7, DIV width
'px;height:',
'', //index 9, DIV height
'px;overflow:hidden;”><img src=”‘,
'', //index 11, IMG URL
'“ width=”‘,
'', //index 13, IMG width
'“ height=”‘,
'', //index 15, IMG height
'“ alt=”‘,
'', //index 17, IMG alt text
'“></div>'
];
return (function(url, id, width, height, top, left, altText){
buffAr[1] = id;
buffAr[3] = top;
buffAr[5] = left;
buffAr[13] = (buffAr[7] = width);
buffAr[15] = (buffAr[9] = height);
buffAr[11] = url;
buffAr[17] = altText;
return buffAr.join(‘');
}); //:End of inner function expression.
})();

这是来自于dojo类库的一段代码,基本 上,getImgInPositionedDivHtml是一个用来创建绝对定位div的html代码。这个div是有固定的模式的,变动的只是具体的一 些属性,所以它用了一个buffAr来承载这个模式,getImgInPositionedDivHtml在接收到具体的属性值(url, id, width, height, top, left, altText),就会把它放到buffAr的相应位置,再返回buffAr的join后的字符串。而无论 getImgInPositionedDivHtml被执行多少次,buffAr只会被创建一次,类似于静态成员的行为。当然,在真正的面向对向语言中, 我们是不能这么做的,因为还要考虑到线程安全的问题,有点扯远了。

这两段代码,都是利用了Javascript特有的闭包来实现各自的效果,什么是闭包呢?

我们知道,变量有作用域的概念,在一个函数体中,使用var声明局部变量只在函数体中能被引用,而在函数外部则是无法访问该变量的。理论上,局 部变量会在函数执行完毕之后被释放,除非它有在其它地方被引用(也很有可能是在脚本被卸载的时候才统一回收的,但这不影响它能否被引用),最容易理解的就 是被return回上层调用:

1
2
3
4
5
function getLocalVar() {
var localVar = “I’m local var”;
return localVar;
}
var globalVar = getLocalVar();

作用域的概念在很多编程语言中都有存在,在Javascript中一样也有,特别的地方就在于Javascript是一种动态语言,它可以定义内嵌函数。当你在一个函数内部定义一个新的函数时,内嵌函数就可以引用该函数的局部变量。:

1
2
3
4
5
6
function OutterFunction() {
var outterVar;
function InnerFunction() {
return outterVar;
}
}

当OutterFunction每次被调用时,解释器会为它分配一个栈区,所有的局部变量都会被放到这个栈区(闭包开始),栈区内的局部变量都 是相互可见的,而InnerFunction也是局部变量之一,所以当OutterFunction执行完毕退出以后,InnerFunction依然可 以访问到其他的局部变量(闭包形包)。要注意的是InnerFunction每次都被重新创建,每次返回都是不同的。换句话说,闭包就是指返回出来的InnerFunction可以访问到OutterFunction的局部变量这种情形

理解并合理地使用闭包可以使程序更加灵活和优雅。很多Javascript类库都有闭包的应用,所以,如果需要经常和Javascript打交道的话,闭包是无法绕过的概念。

Compartir Comentarios

理解Javascript中类的定义

关于如何在Javascript中定义类,网上可以找到不少的文章。在开始讲之前,还是先来看看定义类的两种基本方式吧:

(1) 利用函数构造类型。

1
2
3
4
5
6
7
function Foo(text, url) {
this.text = text;
this.url = url;
this.render = function() {
document.write(‘<a href=”‘ + this.url + ‘“>’ + this.text + ‘</a>’);
}
}

(2) 利用原型prototype。

1
2
3
4
5
6
7
8
9
function Bar(text, url) {
this.text = text;
this.url = url;
}
Bar.prototype = {
render : function() {
document.write(‘<a href=”‘ + this.url + ‘“>’ + this.text + ‘</a>’);
}
}

代码并不复杂,相信大多数人看完之后都会晓得怎么使用了。但是当我第一次看到这种类的定义方式的时候觉得很奇怪,很长一段时间里都是知其然而不知其所以然。如果你有和我一样的困惑,也许可以分享一下我的一点经验。

先来看看怎么使用定义好的类型,

1
2
3
4
var a = new Foo(‘Link A’, ‘http://www.cnblogs.com');
var b = new Bar(‘Link B’, ‘http://www.csdn.net');
a.render();
b.render();

跟很多面向对象语言(如C#)的使用方法一样,都是使用new关键字实例化类,而且当你用instanceof来测试它们是不是相应类型时,都可以得到预望的答案。两种不同方式定义出来的类型Foo和Bar很多时候使用起来并没有什么不同。

但它们在原理上是完全不同,方式1,是在构造函数中动态中创建类的成员,这就意味着每个Foo的实例它们的render方法其实是两个完全独立 的函数;而方式2,则是通过原型prototype的方式将类的共享成员与类的实例绑定在一起,所以每个Bar实例的render方法都是指向同一个方 法,它是静态的。请看以下代码:

1
2
3
4
5
document.write(Object.prototype==String.prototype);
var c = new Foo(‘Link C’, ‘http://www.asp.net');
document.write(c.render == a.render); // false
var d = new Bar(‘Link D’, ‘http://www.kleshwong.com);
document.write(d.render == b.render); // true

Foo在实例化后就是一个独立的个体,它的任何改动不会影响到原来的类型定义,也不会影响到其他的实例;而Bar则不同,它的所有实例都依然受 prototype的影响,通过对Bar.prototype的修改或扩展,可以影响到所有包括已经实例化的实例。回过头来看Foo,由于它的成员,特别 是成员函数是动态创建的,利用闭包,则可以模拟面向对象中的“私有成员”,这一点内容会很长,稍后有时间再和大家分享。

两种方式各有特点。但是最后,严格来说,无论什么方式,都只能说是“模拟自定义类型”,因为在基于对象的Javascript中,它事实上是没有原生的“自定义类型”的概念的(这一点也很长,暂不细说)。

最后推荐大家一般使用方式2来在Javascript模拟自定义类,一来,速度上会比较快,而且还可以通过prototype对类型进行修改,除非你有需要用到闭包来模拟“私有成员”。

Compartir Comentarios

打开 Freetextbox 的 InsertImageFromGallery 并利用 ftb.imagegallery.aspx 作选图功能

如何打开 Freetextbox 的 InsertFromGallery 功能

eWebEditorWebHtmlEidtor 等许多在线编辑器默认都有一个功能,可以实时把图片上传到Server并插入到编辑器里面,而在 Freetextbox 默 认的“插入图片”只能让你插入一个图片网址,如果你需要实时上传图片和对图片进行设置的话,就要打开强大的 InsertImageFromGallery 功能。虽然网上能搜到不少的相关文章,但基本上都是没用的,估计可能是版本不同的原因,我下载的版本是3.1.6。

首先把下载好的 FTBv3-1-6.zip 解压。然后对应 .Net 的版本 FreeTextBox.dll 添加到 Visual Studio 的工具栏里面,如 .Net 版本是2.0,则在 Visual Studio 2005 里右击“添加选项卡”添加名为“FreeTextBox”的项,右击项在弹出菜单中点击“选择项”,在“.Net Framwork组件”中选“浏览”把 Framework-2.0 里面的 FreeTextBox.dll 添加进去。完成了这一步之后,就可以在任何网站或者Web Project使用FreeTextBox了,只要简单地把工具栏上面的FreeTextBox拖到页面上即可,VS会帮你Copy所需的DLL文件和生 成相应的licenses.licx(这个会在你的应用程序根目录,是FreeTextBox的授权文件)。

为了打开 InsertFromGallery ,必须先从 FTB 的解压目录把 ftb.imagegallery.aspx 拷贝到网站/Web Project目录下,然后,先把 ftb.imagegallery.aspx 的只读属性去掉,因为需要对它进行一些必要的更改,在源代码视图中,把JavaScriptLocation、 UtilityImagesLocation这两项设为InternalResource让它引用DLL的内嵌资源文件。再把 AllowImageDelete、AllowImageUpload、AllowDirectoryCreate、 AllowDirectoryDelete等都设为true。
现在,对页面上的 FreeTextBox 控件进行设置,ImageGalleryPath 指定图片存放的位置(注意要按照此路径建立相应的文件夹),ImageGalleryUrl 指定 ftb.imagegallery.aspx 所在的URL,如果FreeTextBox所在的页与ftb.imagegallery.aspx在同一目录下,则无需设置此项,最后要设置 ToolbarLayout 属性,在 InsertImage 之后加入 InsertImageFromGallery 。
这时用浏览器预览之后会在原来的Insert Image按钮右边多出一个按钮来,通过这个就可以实现上传图片文件并对图片进行适当地设置再插入到编辑器中。

至此,FreeTextBox 的 InsertImageFromGallery 功能就完全打开了。

如何接收 ftb.imagegallery.aspx 返回的图片信息

一般的企业网站很多时候会有产品展示系统,通常会需要独立的图片字段,如缩略图。我们可以利用ftb.imagegallery.aspx 对该字段进行管理。由于使用了InternalResource方式,所以我是通过 Firefox+Web Developer 对Javascript进行观察,当然你也可以使用ExternalFile方式:
先把压缩包内aspnet_client文件夹复制到网站 的根目录,然后对ftb.imagegallery.aspx和Freetextbox进行相应的设置,以Location为结尾的属性设为 ExternalFile,SupportFolder设为”~/aspnet_client/FreeTextBox/“
总的来说,InternalResource使用起来比较方便,不用去复制aspnet_client;ExternalFile方式比较灵活,必要时可以自行对资源文件,如js文件进行修改。

ftb.imagegallery.aspx 的对接方式还是比较简单的,相关的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function FTB_InsertImage() {

image = document.getElementById('img_preview');
src = document.getElementById('img_preview').src;
if (src == '' || src == null) return;

alt = document.getElementById('img_alt').value;
title = document.getElementById('img_title').value;
width = image.width; //document.getElementById('img_width').value;
height = image.height; //document.getElementById('img_height').value;
align = document.getElementById('img_align').options[document.getElementById('img_align').selectedIndex].value;

hspace = document.getElementById('img_hspace').value;
vspace = document.getElementById('img_vspace').value;
border = document.getElementById('img_border').value;

ftb = document.getElementById('TargetFreeTextBox').value;

img = '<img src="' + src + '"' + ' temp_src="' + src + '"' +
( (alt != '') ? ' alt="' + alt + '"' : '' ) +
( (title != '') ? ' title="' + title + '"' : '' ) +
( (width != '') ? ' width="' + width + '"' : '' ) +
( (height != '') ? ' height="' + height + '"' : '' ) +
( (height != '') ? ' height="' + height + '"' : '' ) +
( (align != '') ? ' align="' + align + '"' : '' ) +
( (hspace != '') ? ' hspace="' + hspace + '"' : '' ) +
( (vspace != '') ? ' vspace="' + vspace + '"' : '' ) +
( (border != '') ? ' border="' + border + '"' : '' ) +
' />';

window.opener.FTB_API[ftb].InsertHtml(img);
window.close(); //这个是我加上去的,原始文件没有这行。
};

显然,ftb.imagegallery.aspx 会在用户点击 Insert 按钮之后生成图片的html代码,并调用指定的FTB的InsertHtml方法将代码传递过去。因此,只要为某一元素添加InsertHtml方法,并 附加到 FTB_API 对象上,就可以接收和利用 ftb.imagegallery.aspx 返回的图片代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<asp:TextBox ID="oThumbnail" runat="server" Width="200px"></asp:TextBox>
<input id="Button1" type="button" value="..." onclick="pickThumbnail('<%= oThumbnail.ClientID %>');" />
<script type="text/javascript">
<!--
function pickThumbnail(src_id) {
var o = document.getElementById(src_id);
if (o.InsertHtml === undefined) {
o.InsertHtml = function(img) {
//this.value = img;
var div = document.createElement("DIV");
div.innerHTML = img;
//div.firstChild.src 取得图片的绝对网址。这里处理为绝对根路径。
this.value = div.firstChild.src.substr((location.protocol+"//"+location.host).length);
}
FTB_API[o.id] = o;
}
// rif 代表图片文件夹的根目录,cif 代表当前目录
var gallery = window.open("ftb.imagegallery.aspx?rif=~/images&cif=~/images&ftb="+o.id,'gallery','width=700,height=600,toolbars=0,resizable=1');
gallery.focus();
}
// -->
</script>

以上代码在IE7.0及Firefox测试通过。

PS: ftb.imagegallery.aspx 在点击Insert按钮后是不会自动关闭的。如果想点击此按钮后关闭该窗口。可以改用ExternalFile方式,然后打开 aspnet_clientFreeTextBoxFTB-ImageGallery.js 文件,在FTB_InsertImage函数第77行,也就是函数结尾最后加上window.close();

Compartir Comentarios

如何整合Vista IIS7 与 Tomcat 5.5

IIS整合Tomcat的目的,就是要在IIS上把所有关于JSP的请求转发到Tomcat上处理,再把结果发送给客户端,从外部看来,就好像是 IIS支持JSP一样.而配置的方法,网上虽然能找到很多,但是由于IIS7相对于以往改变较大,所以配置过程还是遇到了不少麻烦.

1. 安装JDK

安装JDK 1.5Tomcat 5.5,生产环境建议安装exe版本以服务的方式运行,JDK和exe版的Tomcat基本不需要配置.而我这边主要是用以调试,所以是下载zip文件形式的Tomcat.相关的配置方法,可以参考这篇文章,注意IIS和Tomcat都使用各自默认的端口80和8080.

2. 配置Tomcat

在IIS中把JSP相关的请求发送到Tomcat需要Tomcat Connector的支持.事实上,Tomcat为主流的 Web Server 和 OS 都提供了支持.这应该是由于Tomcat将自己定位为JSP容器,而非 Web Server,即使它也可以作为 Web Server 使用.在上面的链接中下载 isapi_redirect.dll ,我把它放到了 Tomcat 目录里面的 binwin32i386 里面.然后参考这里对 Connector 进行配置,我这里使用了注册表的方式,上面还有另一种通过properties文件进行配置的方法.这里要注意的是,虽然上面说在 Tomcat 的 conf 目录里能找到示例的配置文件,但我在tomcat里面却没有发现相关的文件.这里给出 workers.propertiesuriworkermap.properties简单示例
worker.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# workers.properties.minimal -
#
# This file provides minimal jk configuration properties needed to
# connect to Tomcat.
#
# The workers that jk should create and work with
#
worker.list=ajp13w

#
# Defining a worker named ajp13w and of type ajp13
# Note that the name and the type do not have to match.
#
worker.ajp13w.type=ajp13
worker.ajp13w.host=localhost
worker.ajp13w.port=8009

uriworkermap.properties

1
2
3
4
5
6
7
8
9
10
11
# uriworkermap.properties - IIS
#
# This file provides sample mappings for example ajp13w
# worker defined in workermap.properties.minimal
# The general sytax for this file is:
# [URL]=[Worker name]

/servlets-examples/*=ajp13w

# Optionally filter out all .jpeg files inside that context
# For no mapping the url has to start with exclamation (!)

workders.properties中 可以定义多个worker,若要添加worker,首先要修改worker.list项,多个worker使用,号分隔,然后再通过 worker.<worker_name>.<property_name>的形式定义workder的属性.如上例中的 ajp13w,其中type表示Tomcat端使用的协议,除了ajp13外,常见的还有旧版的ajp12协议,host自然就是指tomcat所在的主 机,我的理解是应该可以把它指到其他的主机上面,实现负载均衡等高级应用.port一般不需要更改,要注意这个port是提供ajp13服务的,与 8080的http是两回事.我在配置的时候就发生ajp13服务没有正常启动,重启了Server之后才解决,要确认该Server有无启动,可以查看logs下面 的stdout开头的日志文件,基有发现”INFO: JK2: ajp13 listening on /0.0.0.0:8009” 字样的行,说明Tomcat的 ajp13已经正常运行.
uriworkermap.properties的 配置方法也很简单,等号的左边是url的样式,右手边就是worker的名称.Connector会把匹配该样式的请求转发到指定的worker去进行处 理.这里相当于在IIS把/servlets-examples/映射到Tomcat的/servlets-examples/应用程序.你也可以把 IIS里面的站点根目录和Tomcat里面的站点根目录指到同一个目录,然后再把.jsp和.do映射到Tomcat去处理,这种方法应该比较适合生 产环境,因为据说tomcat对静态文件的处理能力较差.

配置IIS

现在开始对IIS进行配置,首先打开 IIS管理器 ,ISAPI和CGI限制 里面把isapi_redirect.dll添加进去,并设为允许,如果果你找不到这一项,请点击右边树形菜单的根选项,即是以计算机名称开头的项.只有在这里才能找到.这个相当于IIS6里面的”Web 服务扩展”,要在这里有设定为允许的项才能在IIS里面被执行.
完成这一步之后,再在网站里面的”Default Web Site”添加应用程序.程序名应对应于在注册表中extension_uri的设置.此处为”jakarta”,然后将物理路径指向isapi_redirect.dll所在的目录.完成之后点击这个应用程序,进入处理程序映射,点击右手边的”编辑处理程序权限…“,把”执行”项勾打上.
随后为站点添加ISAPI筛选器以便Connector能截取到URL请求.点击”Default Web Site” -> “ISAPI 筛选器” ,把isapi_redirect.dll添加进去,名称可以随便起.
完成这个步骤以后,Connector就可以对请求进行过滤,把符合配置文件中的请求重写到extension_uri指定的uri进行处理,这里为/jakarta/isapi_redirect.dll,然后isapi_redirect.dll再转发到tomcat的8009由tomcat处理完成后再循原路发回去.

如对 workers.properties 进行更改,需要重启IIS.另外VISTA下面似乎uriworkermap.properties的动态加载功能也无法正常运行.每次更改也需要重启IIS..

Compartir Comentarios

.Net 1.1/.Net 2.0共存主机引起的.Net 2.0程序无法正常运行

随着.Net 2.0的正式推出。目前,许多主机供应商已经为其.Net1.1的主机加上.Net 2.0的支持。想必是由于成本以及兼容性的考虑,很多主机商为将主机配置为.Net1.1/.Net2.0共存的状态。这样的配置会引发一些问题可能会引起.Net 2.0网站无法正常运转。

1、IIS不会动态地辨认您的程序是1.1还是2.0的。因此,假如您的程序是2.0的,可能需要手动地设置站点使用.Net2.0 FrameWork

2、所有由WebResource.axd输出的内嵌资源文件可能会无法正常的输出,比如脚本文件,图片文件等。造成这个现象主要是因为 .axd 的ISAPI映身没有配置。而是只支持.Net2.0的主机上是有这一项的,如何形成还是一个谜。解决办法也很简单,将.axd映射配置与.aspx同一个dll即可。

Compartir Comentarios

解决使用UTF-8编码/JMail组件发送邮件乱码

很久之前听一位朋友讲过,使用UTF-8编码发送邮件,会有乱码的情况发生。近日为公司Programming支持多语言的网站,正好要使用UTF-8编码。网站有会员注册,查询表单等模块皆需要发送邮件给注册用户或者管理员。心里寻思着免不了要和这乱码问题起正面冲突了。

奇怪的是,项目一直顺利地进行的,虽然邮件中的中文在网页里查收确实是会乱码。但是用Outlook或者Foxmail收取之后在本地查看却很正常。哈,莫非友人所指的乱码便是如此?看来问题不大,我便将网站上传到Server交由另一组去测试了。

第二天,正当我悠哉游哉地在听歌看文章时,那边同事反馈过来说收到邮件会乱码。心下吃了一惊了,立马试了一下,果然,用Foxmail收到打开之后还是 一陀问号。循例Google了一翻,想看一下有没有人遇到过同样的问题,但没什么发现。之后又去dimc.net(Jmail的官网)的论坛晃了半天,也 没能解决问题。看来只有靠自己了。。。

经过两个小时不断地在Server上修改代码发送邮件测试,问题并无改善。

于是开始从本地电脑与Server上面的差异入手,先是发现jmail的版本不一致,便更新了Server的jmail版本。无效。感觉问题应该不是出在网站程序和 Jmail组件上。为了确定问题的关键,在邮件发送之前将已经赋给Jmail的内容输出,果然,在这里邮件的内容已经乱码。我的操作系统装的是简体中文 版,Server上面的是英文版,最后。尝试在区域与语言选项里面的高级,非unicode程序的语言 上面选用了中文,重启Server之后问题终于解决。。。

Compartir Comentarios

让.Net2.0的Membership使用已存在的Sql Server2000/2005数据库

VS2005 ASP.NET配置工具可以很方便地配置和使用Membership功能,如果你有安装 Sql Server Express 的话,ASP.NET配置工具会帮你生成数据库及更改Web.Config文件以便使用membership的各种功能。

但是如果没有安装 Sql Server Express 的话,或者是想使用已存在的数据库的话,你会发现ASP.NET配置工具根本就没办法帮你初始化非Sql Server Express数据库。这个时候,就要手动地进行MemberShip的配置工作了。

首先,打开“Visual Studio 2005 命令提示”(在VS2005菜单里面的Visual Studio Tools里面能找到),运行aspnet_regsql.exe这个工具。然后按照屏幕提示,初始化数据库。

完成数据库的初始化工作之后,就需要对Web.Config进行相应配置。首先,确保在connectionStrings节里面已经配置好可以连接到该数据库的ConnectionString。如:

1
2
3
4
5
<configuration xmlns="[http://schemas.microsoft.com/.NetConfiguration/v2.0](http://schemas.microsoft.com/.NetConfiguration/v2.0)">
<connectionStrings>
<add name="northWindConn" connectionString="server=localserver;uid=sa;pwd=;Initial Catalog=northWind" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>

接下来,配置membership节和roleManager节,这两个节都拥有providers子节,可以为他们创建多个 provider,每个provider都有connectionStringName的属性,这个属就是与上面connectionStrings相关 联的。以下是示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<configuration xmlns="[http://schemas.microsoft.com/.NetConfiguration/v2.0](http://schemas.microsoft.com/.NetConfiguration/v2.0)">
<system.web>

<membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
<providers>
<clear />
<add
name="SqlProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="northWindConn"
applicationName="northWind"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="true"
requiresUniqueEmail="true"
passwordFormat="Hashed" />
</providers>
</membership>

<roleManager defaultProvider="SqlProvider"
enabled="true"
cacheRolesInCookie="true"
cookieName=".ASPROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="false"
cookieSlidingExpiration="true"
cookieProtection="All" >
<providers>
<add
name="SqlProvider"
type="System.Web.Security.SqlRoleProvider"
connectionStringName="northWindConn"
applicationName="northWind" />
</providers>
</roleManager>
</system.web>
</configuration>

.Net 2.0的MemberShip被设计为可以通地过一个数据库为多个应用程序提供MemberShip服务。不同的applicationName配置取得的资料是不同的,因此配置时需要注意前后提供的applicationName要一致。

完成了以上两步,就可以通过VS2005自带的ASP.NET配置工具来进行角色,用户等的管理了。

Compartir Comentarios