阅读完本文后, 你将可以自定义InfoPath Logic Inspector, 并且能够轻松地做到下图所示的改动.
 
InfoPath 揭秘 (一)_Hack
 
我将会以一个更复杂且更实用的例子来示范如何自定义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 揭秘 (一)_InfoPath_02

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 揭秘 (一)_揭秘_03 

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

 

InfoPath 揭秘 (一)_Logic Inspector_04 

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

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

InfoPath 揭秘 (一)_揭秘_05

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

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

Conclusion

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