根据InfoPath team blog里介绍的File Attachment Control的内容,写了两个类来处理文件的编码与解码。
public class InfoPathAttachmentEncoder

...{
private string base64EncodedFile = string.Empty;
private string fullyQualifiedFileName;

/**//// <summary>
/// Creates a encoder to create an InfoPath attachement string
/// </summary>
/// <param name="fullyQualifiedFileName"></param>
public InfoPathAttachmentEncoder(string fullyQualifiedFileName)

...{
if (fullyQualifiedFileName == string.Empty)
throw new ArgumentException("Must specify file name", "fullyQualifiedFileName");
if (!File.Exists(fullyQualifiedFileName))
throw new FileNotFoundException("File does not exist: " + fullyQualifiedFileName, fullyQualifiedFileName);
this.fullyQualifiedFileName = fullyQualifiedFileName;
}

/**//// <summary>
/// Returns a Base64 encoded string
/// </summary>
/// <returns>String</returns>
public string ToBase64String()

...{
if (base64EncodedFile != string.Empty)
return base64EncodedFile;
// This memory stream will hold the InfoPath file attachment buffer before Base64 encoding
MemoryStream ms = new MemoryStream();
// get the file information
using (BinaryReader br = new BinaryReader(File.Open(fullyQualifiedFileName, FileMode.Open, FileAccess.Read, FileShare.Read)))

...{
string fileName = Path.GetFileName(fullyQualifiedFileName);
uint fileNameLength = (uint)fileName.Length + 1;
byte[] fileNameBytes = Encoding.Unicode.GetBytes(fileName);
using (BinaryWriter bw = new BinaryWriter(ms))

...{
// Write InfoPath attachment signature

bw.Write(new byte[] ...{ 0xC7, 0x49, 0x46, 0x41 });
// Write the default header information
bw.Write((uint)0x14); // size
bw.Write((uint)0x01); // version
bw.Write((uint)0x00); // reserved
// Write the file size
bw.Write((uint)br.BaseStream.Length);
// Write the size of the file name
bw.Write((uint)fileNameLength);
// Write the file name (Unicode encoded)
bw.Write(fileNameBytes);
// Write the file name terminator (which is two nulls in Unicode)

bw.Write(new byte[] ...{0,0});
// Iterate through the file reading data and writing it to the outbuffer
byte[] data = new byte[64*1024];
int bytesRead = 1;
while (bytesRead > 0)

...{
bytesRead = br.Read(data, 0, data.Length);
bw.Write(data, 0, bytesRead);
}
}
}
// This memorystream will hold the Base64 encoded InfoPath attachment
MemoryStream msOut = new MemoryStream();
using (BinaryReader br = new BinaryReader(new MemoryStream(ms.ToArray())))

...{
// Create a Base64 transform to do the encoding
ToBase64Transform tf = new ToBase64Transform();
byte[] data = new byte[tf.InputBlockSize];
byte[] outData = new byte[tf.OutputBlockSize];
int bytesRead = 1;
while (bytesRead > 0)

...{
bytesRead = br.Read(data, 0, data.Length);
if (bytesRead == data.Length)
tf.TransformBlock(data, 0, bytesRead, outData, 0);
else
outData = tf.TransformFinalBlock(data, 0, bytesRead);
msOut.Write(outData, 0, outData.Length);
}
}
msOut.Close();
return base64EncodedFile = Encoding.ASCII.GetString(msOut.ToArray());
}
}
public class InfoPathAttachmentDecoder

...{
private const int SP1Header_Size = 20;
private const int FIXED_HEADER = 16;
private int fileSize;
private int attachmentNameLength;
private string attachmentName;
private byte[] decodedAttachment;

/**//// <summary>
/// Accepts the Base64 encoded string
/// that is the attachment
/// </summary>
public InfoPathAttachmentDecoder(string theBase64EncodedString)

...{
byte [] theData = Convert.FromBase64String(theBase64EncodedString);
using(MemoryStream ms = new MemoryStream(theData))

...{
BinaryReader theReader = new BinaryReader(ms);
DecodeAttachment(theReader);
}
}
private void DecodeAttachment(BinaryReader theReader)

...{
//position the reader to get the filesize
byte[] headerData = new byte[FIXED_HEADER];
headerData = theReader.ReadBytes(headerData.Length);
fileSize = (int)theReader.ReadUInt32();
attachmentNameLength = (int)theReader.ReadUInt32() * 2;
byte[] fileNameBytes = theReader.ReadBytes(attachmentNameLength);
//InfoPath defaults to UTF8 encoding.
Encoding enc = Encoding.Unicode;
attachmentName = enc.GetString(fileNameBytes, 0, attachmentNameLength - 2);
decodedAttachment = theReader.ReadBytes(fileSize);
}
public void SaveAttachment(string saveLocation)

...{
string fullFileName = saveLocation;
if(!fullFileName.EndsWith(Path.DirectorySeparatorChar.ToString()))

...{
fullFileName += Path.DirectorySeparatorChar;
}
fullFileName += attachmentName;
if(File.Exists(fullFileName))
File.Delete(fullFileName);
FileStream fs = new FileStream(fullFileName, FileMode.CreateNew);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(decodedAttachment);
bw.Close();
fs.Close();
}
public string Filename

...{

get...{ return attachmentName; }
}
public byte[] DecodedAttachment

...{

get...{ return decodedAttachment; }
}
}
这样,我们就能根据需要对File Attachment Control来做进一步的控制了。
前两天通过Live Meeting作了一个远程的Demo,是关于Microsoft Office Solution Accelerator for Proposals的。被录了下来,放在这里:http://www200.placeware.com/cc/microsoft/view,大家有兴趣的话可以看看,Meeting ID: DSD5K9 Meeting PWD: RRJWNK
这个Demo的内容比较浅,主要是演示Solution Accelerator for Proposals的功能,没有涉及到定制和扩展的内容。由于用Live Meeting作远程Demo的经验不是很足,整个Demo的过程显得有点结结巴巴的,而且其中还有几个说错的地方。大家看看就行了,不要取笑偶
上个月的大部分时间一直在忙着换工作的事情,很久没有更新自己的blog了。今天闲下来,汇报一下自己的近况吧。
从5月6日起,我已经正式On Board,开始在微软GTEC工作了。如果我没搞错的话,我应该是和Grace在同一层楼工作,由于6、7两天Grace还在休假,没能见着,不过星期一就能见了
。
换工作真是一件耗费精力的事情。上个月我基本上就是在面试、拿Offer、体检、辞职、办离职手续中度过的。等待消息总是让人很焦虑,不能定下心来做事情,比如写blog,每次写的时候思路都很混乱,最后都放弃了。因此上个月整整一个月基本上没做什么正经的事情。还好我以前的公司还比较能够体谅我,虽然交了一笔不菲的违约金,但总算没有让我在辞职之后等上一个月才能走,否则我真不知到在等待的日子中能做点什么。
我目前的工作是Support,在这方面我是比较缺少经验的,所以在未来的一段日子里我肯定会有很多东西需要学习,不知道还能不能常常上来更新了,尽量争取吧。
说实话,微软能够成功是有他的道理的。通过两天的工作,我发现我的同事都是非常优秀的,这么好的资源在其他一些中小公司中是很难想象的。更为关键的,微软的每个人对工作都很积极主动,很有热情。我还不知道其中的动力是从哪里来的,不过我想我会找到答案的。
BTW,这里我只是说说自己的近况,如果你对微软很反感,或者想骂微软,请嘴下留情吧,怕了。
最近由于项目的原因,需要在Win9x平台上做一些底层的工作。Win95/Win98虽然是32位的操作系统,但是为了兼容16位应用程序,其中仍然保留着相当数量的16位程序,一些系统调用,最终还是通过16位程序完成的。这样,在Win9x平台上,一些底层的应用就会涉及32位代码和16位代码之间的互相调用。
微软为16位与32位代码间的互操作提供了一个标准的解决方案,称作Flat Thunk(在微软的技术中,有很多地方都用到了thunk这个词,比如在ATL中也有一个thunk,用来实现窗口句柄和窗口类之间的映射。Thunk在金山词霸中的解释是“形(式)实(在)转换程序”,侯捷在某处将它译为“一小段程序”)。简单说来,Flat Thunk由一个thunk脚本、一个32位dll和一个16位dll组成,这两个dll中含有thunk compiler生成的32位和16位连接代码,避免了由程序员自己来处理32位和16位代码转换时复杂的堆栈管理、内存对齐等工作。KB中有两篇文章解说的很详细:
HOWTO: Call 16-bit Code from 32-bit Code Under Windows 95, Windows 98, and Windows Me
HOWTO: Call 32-bit Code from 16-bit Code Under Windows 95, Windows 98, or Windows Millennium Edition
其中要用到一些比较古老的工具,现在的Platform SDK中已经没有了,比如VC1.52、Thunk Compiler等。如果一直有订阅MSDN,到以前的MSDN中找找,可能能够找到。
这两天公司在美国那边的一个OnSite来上海和我们讨论上一阶段工作中存在的问题,以便下一阶段改进。这位老兄是个印度人,看样子果然是见过市面的,巨能侃,再加上我们对印度那边的过程管理本就有点崇拜心理,搞得大家对这老兄佩服的不行。
讨论中,我们说上一阶段需求改得太厉害,OnSite显然有责任。这位老兄说,那些需求都是客户公司的老板拍脑袋想出来,We developped for him,因此这个他们OnSite也控制不了。他说,知道美国人为什么说The customer is king,而不说The customer is God吗?大家蒙了,这怎么会知道呢?于是他解释道,因为God forgives,而永远不要指望King forgives。看来我们中国人错了,顾客是国王!
不过不得不承认,印度的过程控制是比我们这边强。讨论时,他觉得我们现在使用的方法不好,我们说我们是按照XP的指导原则实践的,甚至有同事向他引用XP书上话来说明问题。没想到他说了一句话,让我感触颇深,他说,这说明XP用在这里不对,XP不适合我们的项目。然后他仔细解说了XP的要求和我们项目的实际情况的差距。试想我们这边的程序员,大家一听XP,立即就会一种近乎膜拜的心理,谁还敢提出质疑?还不是书上怎么说,我们就怎么做吗?这种质疑与挑战的精神恐怕才是我们真正缺少的。
今天调用一个Web Services的时候,碰到了“System.Net.WebException: The underlying connection was closed: Unable to connect to the remote server.”错误。比较妖的是,我用WinForm调用是成功的,而当我用WebForm的时候,却碰到了上面的异常。上网搜了半天,发现很多人遇到这个异常,可是却没有标准的解法,一般有下面几种方法可以试试:
1.如果用了代理,设置正确。
2.如果用了firewall,将firewall禁掉试试。
3.将Lan设置中的Automatically Detect Settings禁掉。
4.重装.Net Framework。
起初我以为不是Proxy的问题,因为WinForm可以成功。于是试了2和3,都没有什么效果,对于4我实在不大想重装.Net Framework。于是抱着试试看的想法,在代码里用WebProxy为WebServices对象加了个proxy,没想到问题竟然就此解决了
今天是我第一次面试别人。在今天之前我只被别人面试过,而从没有面试过别人。我对如何面试应聘者的所有经验,就是公司前两天进行的一次关于Interview的内部培训。
我今天是应聘者所要面对的第一位技术人员,我感觉自己今天比较紧张,面试过程中总是想着培训的内容,和应聘者聊的内容比较生硬,搞的应聘者也紧张起来了。总的感觉我今天还是比较失败的,我怕是我的原因而使应聘者不能很好的发挥,所以我pass了所有应聘者,使得二面的负担比较重
我的面试结束后去旁听了一个同事的面试过程,他和应聘者聊的非常好,应聘者在他那里明显的放松很多,他们一起在白板上讨论问题,甚至由于意见不一而发生了争执。我觉得这样非常棒。看来我还有很多要学的
不知道大家在Interview这方面有什么经验?一起分享分享
这两天在研究用VC来处理Asp.Net登录的问题。这个问题我问过豆腐,他推荐给我一篇很棒的
KB文章,这篇文章是讲如何用WinInet模拟POST请求的,一般来说,对于Asp页面,我们post的请求是:user=**&password=**&action=**,但是同样格式的请求用在Asp.Net页面上就不行了。我用
EffeTech Sniffer跟踪了半天,发现正常情况下,Post给Asp.Net页面的请求多了一个__VIEWSTATE=**,格式如下:__VIEWSTATE=**&user=**&password=**&action=**,如果我在程序中用相同的格式Post请求的话,就能够正常登录了。__VIEWSTATE后面的内容是经过编码的,我不知道它使用的是哪种编码方式(可以确定不是Base64编码),也不大明白用程序如何获得这个__VIEWSTATE。好像这个__VIEWSTATE是会根据Web Server的不同而有所不同的。不知道大家有没有这方面的经验呢?
用VC写程序的时候常常会遇到只在Release Build中才会出现的问题,由于这样的问题在Debug Build中不会出现,因此无法在Debug Build中调试。记得以前在CSDN的VC论坛中常常有人问这样的问题,大家好像也总结了不少,只是时间久了,记不太清楚了
。只有一条印象非常深刻,就是自定义消息的响应函数的返回值一定得是LRESULT,如果是其他类型,比如void,在Debug下没问题,在Release下就有可能引起崩溃。之所以印象深刻,是由于我在这个上面吃过大亏
Fixing Release Build Problems介绍了一些诊断和调试Release Build的方法,但是似乎没提到我说的这一条