阅读完本文后, 你将可以自定义InfoPath Logic Inspector, 并且能够轻松地做到下图所示的改动.
 
InfoPath 揭秘 (一)
 
我将会以一个更复杂且更实用的例子来示范如何自定义InfoPath Logic Inspector.
 
InfoPath Logic Inspector 的局限
 
InfoPath Logic Inspector最令人头痛的事莫过于无法复制其中的内容. 再加上它是一个模式对话框, 对于InfoPath 开发人员来说实在是不方便. 如果能够提供复制当前内容的功能, 将会方便很多.
 
InfoPath Logic Inspector 到底是什么?
 
既然InfoPath没有暴露任何有关Logic Inspector的接口, 那么就需要我们自己探索了. 首先要弄清楚Logic Inspector到底是由什么控件组成的. 使用Visual Studio的工具Spy++, 可以很容易查看到Logic Inspector实际上是一个IE控件.
 

InfoPath 揭秘 (一)

OK. 接下来, 我们要将Logic Inspector中的内容抓取出来. 下面的代码展示了如何获取IE控件中的内容. 在文章的附件中会有完整的代码.


  1. EnumWindows(new EnumWindowsProc(EvalWindow), IntPtr.Zero);  
  2.  
  3.             foreach (IntPtr hwnd in ipHwnds)  
  4.             {  
  5.                 StringBuilder sb = new StringBuilder(256);  
  6.                 GetWindowText(hwnd, sb, 256);  
  7.  
  8.                 IntPtr hwndIPIE = IntPtr.Zero;  
  9.                 IntPtr parentHwnd = hwnd;  
  10.                 String className = String.Empty;  
  11.                 while (!className.Equals("Internet Explorer_Server"))  
  12.                 {  
  13.                     EnumChildProc childProc = new EnumChildProc(EnumChildWindows);  
  14.                     EnumChildWindows(parentHwnd, childProc, ref hwndIPIE);  
  15.                     className = GetClassName(hwndIPIE);  
  16.                     parentHwnd = hwndIPIE;  
  17.                 }  
  18.  
  19.                 IHTMLDocument2 htmlDoc = IEDOMFromhWnd(hwndIPIE);  
  20.                 String htmlText = htmlDoc.body.parentElement.outerHTML;  
  21.  
  22.                 StreamWriter sw = new StreamWriter(String.Format("d:\\{0}.html", sb.ToString()));  
  23.                 sw.Write(htmlText);  
  24.                 sw.Close();  
  25.             }  

以下是从Logic Inspector 抓取的内容:


  1. <html> 
  2. <head> 
  3.     <title>Logic Inspector</title> 
  4.     <meta content="text/html; charset=utf-8" http-equiv="Content-Type"></meta> 
  5. </head> 
  6. <frameset id="idframeset" framespacing="2" border="2" cols="50%,50%"> 
  7. <FRAME id=overallFrame src="res://1033\ipdsintl.dll/InspectorOverallFrames.html" scrolling=yes> 
  8. <FRAME id=fieldFrame src="res://1033\ipdsintl.dll/InspectorFieldFrames.html" scrolling=yes> 
  9. </frameset> 
  10. </html> 

很明显, Logic Inspector 由2个Frame组成: "overallFrame" 和 "fieldFrame". 从名字即可知道"overallFrame"指代Logic Inspector的左半部, "fieldFrame" 指代Logic Inspector的右半部.

如果你足够细心, 就会发现一个奇妙的地方. 这2个Frame的source居然是从一个dll中读取的. 而这个ipdsintl.dll就是揭开InfoPath 秘密的重点.

IPDSINTL.DLL -- InfoPath 的资源文件

首先我们要找到IPDSINTL.DLL到底在哪. 从它的上级目录"1033"来看, 这个文件应该在Microsoft Office文件夹下. 如我所料, 它确实是老老实实呆在这的:

%ProgramFiles%\Microsoft Office\Office12\1033\IPDSINTL.DLL

把这个DLL拖到Visual Studio中, 可以看到其实它是InfoPath重要的资源文件.

 

InfoPath 揭秘 (一) 

还记得2个frame的路径吗? 赶紧看看HTML文件夹下的内容吧:

 

InfoPath 揭秘 (一) 

在这里不仅找到了2个Frame的source, 还有InfoPath所用控件的定义. 接下来的任务就是修改相应的文件, 并且保存回DLL. 再用新的DLL覆盖原来的DLL. 相信这是每个程序员都能做的事, 我就不赘述了.

使用附件中的"INSPECTORFIELD.HTML"文件覆盖IPDSINTL.DLL相应的文件, 可以得到如下效果:

InfoPath 揭秘 (一)

"Copy Text" 可以复制当前Logic Inspector 的文字内容至剪贴板.

"Copy HTML" 可以复制当前Logic Inspector 的HTML内容至剪贴板.

Conclusion

发现了IPDSINTL.DLL后, 我们不仅仅能修改Logic Inspector, 连InfoPath内置的控件也可以修改了. 接下来就需要大家发挥自己的想象力了~