2.6 多线程调用
多线程调用时,需要在应用程序上下文初始化时,加入多线程标志。如下:
TypedTPINITtpinfo = newTypedTPINIT();
tpinfo.flags = TypedTPINIT.TPMULTICONTEXTS;
AppContextac = AppContext.tpinit(tpinfo);
2.7 结构数据传递问题
Tuxedo windows客户端的原始API是面向C语言的,因此在很多的服务器程序的编写时,会采用struct结构来会传递数据的方案。对于C结构体数据,在tuxedo中对应的消息类型应该是CArray, 在用C#制作客户端时,可以采用TypedCArray这个类型来传递数据,其中,需要特别注意的问题是.net中interop操作时的一些技术细节。
下面是一个具体的例子:
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct MYMSGBODY
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string usrname; // char usrname[20]
[MarshalAs(UnmanagedType.U4, SizeConst=4)]
public uint lLogNo; // unsigned long int lLogNo;
[MarshalAs(UnmanagedType.I4)]
public int iRecNum; // int iRecNum;
}
在MSDN上有详细的结构体interop类型对应表可以查阅。这里要解释其中几个重要的地方:
l StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)
n Pack=4表示以最大4字节边界对齐成员。
n CharSet = CharSet.Ansi表示字串是ansi字串。之后再详述。
l MarshalAs(UnmanagedType.U4, SizeConst=4)
此属性标记为此string成员是以值传递的,也就是是一个数组,而不是指针。并指定了长度。这个长度是指C串里包括了结尾0的总长度。
l Long类型:这个类型在C与.net里的有重要不同,在C中, int的大小根据平台不同有16位长,32位长,(在windows和现代的unix中,一般都是32位), long的长度在windows和unix中一般是32位的,而在.net中,long类型的长度是64位,并且interop很多操作不支持对long型数据的转换,如对结构体取size时,如果有long型字段,就会出现异常,这里要注意。
l 内存分配的对齐问题
这个问题是最难以讲清楚的问题。在C语句的编译器中,都会有一些关于结构体成员如何对齐地址的编译指令或是伪指令,如VC的#pragma pack, __declspec( align() )指令等。这些指令指示编译器如何在内存中排布结构体的成员。
其中,pack=n的意思是:结构体中下一个成员的起始地址,要用“成员类型的长度和n之中的比较小的那个”来对齐。比如说下面的结构体成员:
#program pack(8)
Struct ST_E1{
char s1[2]; // 从0偏移开始, 占到1位置,共2字节
int i; // min(sizeof int= 4,pack=8)=4, 因此,i的起始地址应该按4对齐
// 也就是空两个字节,到偏移4处开始,到偏移7,共4字节
char s2[3]; // min(sizeof char=1, pack=8) = 1, 因此s2的起始地址按1对齐,
// 也就是从偏移8开始,到10,共3字节。
Char s3 // 同理,s3占偏移量11,一个字节
}
结构体总长度为12个字节长。
Off
| 0
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
|
V
| S1
| S1
| 空
| 空
| i
| i
| i
| i
| S2
| S2
| S2
| S3
|
如果pack指为1呢?那么分配的方式如下:
Off
| 0
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
|
V
| S1
| S1
| i
| i
| i
| i
| S2
| S2
| S2
| S3
|
共10个字节长。
可以看出,如果两边的pack值不一样,那么这个结构体在送到目的地之后就会出现成员偏移乱掉的问题(开始想念web services了吧?但是我们不总是能选择所处的条件的)。因此,一定要检查服务器与客户端的这个编译选项是不是一样的。一般情况下,pack=4是比较常见的情况。
有关更多的内存对齐方式的讨论,可以参见本人另一博客文章及其评论内容。
http://www.cnblogs.com/haoxiaobo/archive/2005/09/05/230204.html
l 字符集问题
字符集是另一个需要注意的兼容性问题。例如上面的成员定义:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string usrname; // char usrname[20]
在大多数面向C语言的API中,基本上是没有char与byte的区别的,但是.net中对于char与byte则有本质的不同,char是指一个与文化相关的符号,而byte指一个8位二进制数的物理存储单位。一个char需要几个byte来保存,要视字符集编码方式而定,在.net和java里,内部的char都是unicode,一个字符两个字节,而在C中,基本上都是ansi(除了windows nt之后新增的那些 _T类型)。
由于这个原因,一定要认真考虑服务器系统的字符集编码,否则会导致字符串在interop转换时,产生非常令人生气的结果。
在结构体的定义时,charset = charset.ansi即通知了interop程序,字符串在向结构体转换时,要用ansi方式进行转换。
更多有关字符集的讨论,请参见本人另一篇博客文章:
http://xiaobohao.spaces.live.com/blog/cns!D1C72860197EBF38!1250.entry
完整的用结构体内存块数据做消息体,调用tuxedo服务的代码如下:
Bea.Tuxedo.ATMI.Utils.tuxputenv("WSNADDR=//10.1.128.227:9401");
Bea.Tuxedo.ATMI.Utils.tuxputenv("WSINTOPPRE71=yes");
AppContext ac = AppContext.tpinit(null);
RecivedStruct rec; // 这个RecivedSTRUCT即是tuxedo服务所规定的返回消息结构体在C#里的对应定义,请参阅本节之前的说明,对C风格的结构进行C#定义。
SendStruct app = new SendStruct ();// 这个SENDSTRUCT即是tuxedo服务所规定的调用消息结构体在C#里的对应定义,请参阅本节之前的说明,对C风格的结构进行C#定义。
app.sOperCode = “…”; // 几个示意成员
app.sDeptCode = “…”;
//…
// 开始把C#结构体的内容复制为byte[]。
// 取得长度
intiAppLen = Marshal.SizeOf(app);
int iRecLen = Marshal.SizeOf(typeof(RecivedStruct));
TypedCArray tbSend = new TypedCArray(iAppLen);
TypedBuffer tbRecive = new TypedCArray(iRecLen);
byte[] arAppData = new byte[iAppLen];
// 分配一个系统堆内存, 并用于一个指针来指向之
IntPtr pApp = Marshal.AllocHGlobal(iAppLen);
IntPtr pRec= Marshal.AllocHGlobal(iRecLen);
// 将托管结构复制到此地址指向的内存块中。
Marshal.StructureToPtr(app, pApp, false);
// 再将此地址块复制到字节数组中。
Marshal.Copy(pApp, arAppData, 0, iLen);
//把此字节数组绑定到要发送数据中。
tbSend.PutBytes(arAppData);
try
{
// 调用服务,返回一个typedbuffer.
ac.tpcall(sServiceName, tbSend, ref tbRecive, 0);
// 开始从这个返回的内容里取出数据。
// 初始化一个与返回值相同大小的数组。
byte[] arRecived = newbyte[tbRecive.Size];
// 从返回值对象中取出字节数组。
((TypedCArray)tbRecive).GetBytes(arRecived, arRecived.Length);
// 用相反的步子把数据从字节流中复制到C#结构中。
Marsal.Copy(arRecived, 0, pRec, iRecLen);
Rec = Marsal.PtrToStructure(pRec, typeof(RecivedStruct));
}
catch (TPException tpex)
{
Trace.TraceError(tpex.ToString());
}
finally
{
ac.tpterm();
Marshal.FreeHGlobal(pApp);
Marshal.FreeHGlobal(pRec);
}