PINVOKE.NET: Do interop the wiki way!

PINVOKE.NET: Do interop the wiki way!

Writen by Allen Lee

首先什么是PInvoke呢?PInvoke是Platform invoke的缩写。我们先来看看MSDN文档中的解释:

Platform invoke is a service that enables managed code to call unmanaged functions implemented in dynamic link libraries (DLLs), such as those in the Win32 API.

很明显,PInvoke是为了让我们能够调用现有的本机代码,而这些代码是以二进制DLL的形式存在的。微软意识到这种交互的重要性,也深知开发人员不可能丢弃所有已存在的东西直接转向.NET,于是在.NET里面加入了PInvoke这个特性。

那么,什么又是PINVOKE.NET呢?我们来看看官方网站上面的这句话:

Think of this as the 21st century version of VB6's API Text Viewer.

用过VB6的朋友应该对那个API Text Viewer不陌生了吧!在VB6的时代,要调用那些Win32 API函数,就需要用到这个东东。然而在.NET时代却没有一个类似的东西吗?当然,现在你知道这个问题的答案了——PINVOKE.NET。或许,你还发现在它的官方网站上面,我们还可以看到一个很醒目的标语:

Do interop the wiki way!

好吧,赶快下载一个PINVOKE.NET并且安装好,这个步骤的细节我就不啰嗦了。有几点要提醒的,PINVOKE.NET是以插件的形式插入Visual Studio .NET的,所以,前提是,你必须有Visual Studio .NET的其中一个版本,这里用Visual C# .NET来做示范。

首先介绍一下这次的示范项目,它是一个叫做PInvokeLab的ConsoleApplication Project,我们将会调用MessageBox(...)这个API来向用户说声“Hello, PINVOKE.NET!”。现在就让我们来感受一下怎么Do interop the wiki way!

在你需要插入Win32API Signature(有人把它翻译成签名,有人把它翻译成型构,我看我还是用回原文)的地方单击鼠标右键,有没有留意到这个弹出的菜单顶部多了两项?没错,这两项就是PINVOKE.NET插到Visual Studio .NET的东西:“Insert PInvoke Signatures...”和“Contribute PInvoke Signatures and Types...”。

o_PInvokeMenu.JPG

接着选择“Insert PInvoke Signatures...”,得到下面的对话框。

o_PInvokeMain.JPG

在“What function do you need?”那里写入“MessageBox”,按下右边的[Go]按钮,PINVOKE.NET将会自动连接到www.pinvoke.net的数据库查找数据。

o_PInvokeConnecting.JPG

查找完毕就会返回结果。

o_PInvokeResult.JPG

在这个带有结果的截图上,我们可以看到很多东西。第一,有MessageBox的简要介绍,包括功能以及参数的介绍(英文的)。第二,MessageBox的Signature的最后更新日期。第三,将要插入到你的源代码的代码片断。第四,编程语言的选择,由于这里使用C#,所以它将给出C#样式的Signature。不过笔者特意另外创建一个VB.NET的Project,发现它不能以VB.NET的语法样式插入相应的Signature,不知道它是否偏心C#,希望它在将来的版本有所改进,让更多的开发者受益。第五,左下角那里,给出提供类似或相同功能的Managed API作为使用建议,如果有的话,这里是System.Windows.Forms.MessageBox.Show()。

接着,你检查一下,没问题就按下[Insert]键。

o_PInvokeInserted.JPG

它将提示你代码已经被插入了,现在你可以按[Close]回到原源代码那里,并整理一下代码的缩进等。整理后的代码如下:

None.gifusingSystem;
None.gif
usingSystem.Runtime.InteropServices;
None.gif
None.gif
namespacePInvokeLab
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
classTester
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif[STAThread]
InBlock.gif
staticvoidMain(string[]args)
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gifMessageBox(
0,"Hello,PInvoke.NET","PInvokeLab",0);
ExpandedSubBlockEnd.gif}

InBlock.gif
InBlock.gif[DllImport(
"user32.dll",CharSet=CharSet.Auto)]
InBlock.gif
publicstaticexternintMessageBox(inthWnd,Stringtext,Stringcaption,uinttype);
InBlock.gif
ExpandedSubBlockEnd.gif}

ExpandedBlockEnd.gif}

现在编译该代码并查看一下运行结果:

o_PInvokeLab.JPG

好吧,说到现在应该怎么做你也应该很清楚的了,还不赶快Do interop the wiki way!?

最后要对PINVOKE.NET补充一下,菜单的另外一项是指你自己对Win32API描述的Signature上传到www.pinvoke.net的数据库。另外,你也可以去官方那里查看相关的Win32API的说明,这些说明大部分都包括Signature、参数信息、示范例子、可选Managed API建议等(英文),当然你也可以去参考其他的文献,例如MSDN Library。有时候你可能发现某些PInvoke的调用失灵,例如ExitWindowsEx(...),不用慌张,你查看该函数的文档的时候会发现调用该函数还需要一些Windows安全措施例如权限制定等,所以,在你调用Win32API之前,首先得确定调用条件,否则程序可能会有莫名其妙的运行效果。还有一点要提醒的,虽然使用PInvoke可以调用Win32API,但这是需要牺牲一些运行时性能的,所以如果有对应的Managed API的话,我建议你还是选用Managed API。

展开阅读全文

The Ninja Way

02-04

DescriptionnAs we all know, Ninjas travel by jumping from treetop to treetop. A clan of Ninjas plans to use N trees to hone their tree hopping skills. They will start at the shortest tree and make N-1 jumps, with each jump taking them to a taller tree than the one they?re jumping from. When finished, they will have been on every tree exactly once, traversing them in increasing order of height, and ending up on the tallest tree.nnThe ninjas can travel for at most a certain horizontal distance D in a single jump. To make this as much fun as possible, the Ninjas want to maximize the distance between the positions of the shortest tree and the tallest tree.nnnnThe ninjas are going to plant the trees subject to the following constraints.nnAll trees are to be planted along a one-dimensional path.nTrees must be planted at integer locations along the path, with no two trees at the same location.nTrees must be arranged so their planted ordering from left to right is the same as their ordering in the input. They must NOT be sorted by height, or reordered in any way. They must be kept in their stated order.nThe Ninjas can only jump so far, so every tree must be planted close enough to the next taller tree. Specifically, they must be no further than D apart on the ground (the difference in their heights doesn?t matter).nnGiven N trees, in a specified order, each with a distinct integer height, help the ninjas figure out the maximum possible distance they can put between the shortest tree and the tallest tree, and be able to use the trees for training.nnInputnThere will be multiple test cases. Each test case begins with a line containing two integers N (1 ≤ N ≤ 1000) and D (1 ≤ D ≤106). The next N lines each contain a single integer, giving the heights of the N trees, in the order that they should be planted. Within a test case, all heights will be unique. The last test case is followed by a line with two 0's.nnOutputnFor each test case, output a line with a single integer representing the maximum distance between the shortest and tallest tree, subject to the constraints above, or -1 if it is impossible to lay out the trees. Do not print any blank lines between answers.nnSample Inputnn4 4n20n30n10n40n5 6n20n34n54n10n15n4 2n10n20n16n13n0 0nnSample Outputnn3n3n-1 问答

Interop开发Excel(二)

08-29

从这里开始,我将和大家一起讨论Excel的Interop编程。我的参考资料大部分来自于MSDN的一篇文章:Understanding the Excel Object Model from a .NET Developer's Perspective。rnrn Excel提供了上百个对象,可以用来与用户的程序进行交互。但是作为开始,我们应该着重注意如下的一些对象:rnrn * Applicationrn * Workbookrn * Worksheetrn * Chartrn * Rangernrn 使用Delphi .NET以及C#开发Office Interop程序的一个(相比用VB开发的)不便之处在于VB在函数调用时是支持“可选参数”的,而我们即将会看到,对于Excel的Interop模型来说,一个方法往往有多达30多个可选参数。VB编程时可以简单的跳过,只提供必要的参数,而C#/Delphi则不行,必须将函数的参数全部传递进去,哪怕传递的就是Type.Missing。rnrn 本文将首先讨论Application对象。对于Application对象,我们可以将其成员分为如下几类:rnrn * 控制Excel状态及显示的成员;rn * 返回对象的成员;rn * 执行动作的成员;rn * 处理文件的成员;rn * 其它成员rnrn 让我们来分别进行讨论。rnrn 控制Excel状态及显示的成员rnrn 表1列出了控制Excel状态的成员:rn属性 类型 说明rnCursor XlMousePointer (xlDefault, xlIBeam, xlNorthwestArrow,rnxlWait) 设置鼠标的显示rnEditDirectlyInCell boolean 设置是否能在单元格内直接编辑。如果是false,那么将只能在公式栏中进行修改。rnFixedDecimal boolean 如果为true,那么所有的数字都将使用FixedDecimalPlaces属性的值显示固定位数的小数点后数字;否则就将忽略FixedDecimalPlaces属性的值。rnFixedDecimalPlaces long 在FixedDecimal属性为true的前提下,决定了数值显示的小数点后的位数。rnInteractive boolean 设置用户是否可以通过鼠标和键盘和Excel交互。如果将该属性设置为false,那么请一定要记得在合适的地方重新设置为true。Excel本身是不会重新设置该属性的。rnMoveAfterReturn boolean 如果为true(缺省值),那么在按下回车后,当前选择就移到下一个单元格。rnMoveAfterReturnDirection xlDirection (xlDown, xlToLeft, xlToRight, xlUp) 如果MoveAfterReturn为true,该属性指出了按下回车后,当前选择移动的方向。rnScreenUpdating boolean 如果该属性为true,Excel将在每个方法调用后刷新显示。为了节省时间并让你的应用更加专业,你可以在你的代码运行时关闭显示更新。一定记得在适当的时候重新设置该属性为true,Excel是不会重新设置该属性的。rnSheetsInNewWorkbook long 设置了新建工作簿时包含的工作表的数量。rnStandardFont String 设置Excel缺省缺省字体。在重新启动Excel前不会起作用。rnStandardFontSize rnrnlongrn 设置Excel缺省缺省字体的大小。在重新启动Excel前不会起作用。rnStartupPath(只读) String 返回Excel启动时加载项(add-ins)的绝对路径。rnTemplatesPath(只读) String 返回包含模版的目录的绝对路径。这也是Windows的特殊目录之一。rnrn 在上表中,其实我们用的最频繁的应该是ScreenUpdating属性。在该属性为false的时候,程序运行会更快一些,因为频繁更新Excel的显示是很浪费时间的。由于Excel不会重新设置该属性,所以如果你在代码段中将其设置为false,那么一定要确保代码中有一个地方将其重新设置为true。在Delphi/C#中,当然可以通过try/except或者try/finally来做到这点。rnrn Application对象还提供了一组属性来控制Excel的显示。下表是比较常用的几个:rn属性 类型 说明rnDisplayAlerts boolean 如果是true(缺省值),Excel在你代码运行时,在必要时候会显示警告信息:比如删除一个表单的时候。将该值设置为false将跳过这些警告。Excel会认为你选择了每个警告中的缺省返回值。rnDisplayFormulaBar boolean 控制是否显示用来修改单元格的公式栏。缺省是显示。rnDisplayFullScreen boolean 设置为true时,Excel将在全屏模式下运行(和将Excel窗口最大化不同)。缺省是false。rnrn 在Visual Studio .NET中,标准的Microsoft Office工程模版只提供ThisApplication/ThisWorkbook这两个对象。所以在Application对象中包含了若干属性用来返回其它类型的对象。返回的对象可以是单个的,例如ActiveWindow;也可以是个集合,例如Charts。下表列出了返回对象的若干 Application属性:rn属性 类型 说明rnActiveCell Range 返回当前窗口(就是在最前面的窗口)中的活动单元格的引用。如果没有活动窗口,该属性将抛出错误。rnActiveChart Chart 返回当前获得的图表的引用。嵌入图表只有在被选中或激活时才是活动的。rnActiveSheet Object 返回活动工作簿中的活动工作表的引用。rnActiveWindow Window 返回活动窗口(最前面的窗口)的引用。如果没有活动窗口,则返回空。rnCharts Sheets 返回Sheet对象的集合(Worksheet和Chart都继承自Sheet类)。其中包含了活动工作簿中的每个图表。rnSelection Object 返回在应用中被选择的对象。可能是Range、Worksheet或任何其它对象。也适用于Window类,此时通常返回一个Range对象。如果没有对象被选中,那么返回空。rnSheets Sheets 返回活动工作簿中每个表单的引用的集合。rnWorkbooks Workbooks 返回所有打开的工作簿的引用的集合。rnrn 我们用的最多的可能是Workbooks属性。该属性允许我们遍历打开的工作簿,打开或创建工作簿。下面我们详细讨论一下。rnrn * 创建一个新的工作簿rnrn 下面的代码将创建一个新的工作簿:rnrn// Delphi .NETrnrnprocedure TWinForm.Button2_Click(sender: System.Object; e: System.EventArgs);rnbeginrnEWB:=EA.Workbooks.Add(System.Type.Missing);rnend;rnrn// C#rnrnprivate void button2_Click(object sender, System.EventArgs e)rnrnEWB = EA.Workbooks.Add(Type.Missing);rnrnrn 请注意传递的参数:System.Type.Missing/Type.Missing。这个参数在日后将经常出现。所以我会在以后用STM变量来表示——少打几个字也是好的吗。它们可以分别被声明如下:rnrn// Delphi .NETrnrnvar STM: TObject;rnrn...rnrnSTM:=System.Type.Missing;rnrn// C#rnrnObject STM;rnrn...rnrnSTM=Type.Missing;rnrn * 关闭打开的工作簿rnrn 下面的代码可以关闭所有打开的工作簿:rnrn// Delphi .NETrnrnEA.Workbooks.Close;rnrn// C#rnrnEA.Workbooks.Close();rnrn * 打开现有工作簿rnrn 利用Open方法可以打开现有的工作簿。这是一个拥有N多个可选参数的方法,而只有文件名才是必须的。rnrn// Delphi .NETrnrnprocedure TWinForm.Button3_Click(sender: System.Object; e: System.EventArgs);rnvar EWB: Microsoft.Office.Interop.Excel.Workbook;rnvar FN: string;rnbeginrnif (OFD.ShowDialog = System.Windows.Forms.DialogResult.OK) thenrnbeginrnFN:=OFD.FileName;rnEWB:=EA.Workbooks.Open(FN, STM, STM, STM, STM, STM,rnSTM, STM, STM, STM, STM,rnSTM, STM, STM, STM);rnrnend;rnend;rnrn// C#rnrnprivate void button3_Click(object sender, System.EventArgs e)rnrnString FN;rnExcel.Workbook EWB;rnrnif(OFD.ShowDialog()==DialogResult.OK)rnrnFN=OFD.FileName;rnEWB=EA.Workbooks.Open(FN, STM, STM, STM, STM, STM,rnSTM, STM, STM, STM, STM,rnSTM, STM, STM, STM);rnrnrn 我们简单的回顾一下这14个STM参数的含义,有一些参数的含义还是自明的。但可惜的是,BDS中并没有提供这些方法的说明,因此我们只能借助VBA:rnUpdateLinks 打开文件时是否要更新连接内容。rnReadOnly 是否只读。rnFormat 如果Microsoft Excel正在打开一个文本文件,则该参数用于指定分隔字符。rnPassword 该字符串指定打开一个受保护工作簿的密码。如果省略该参数并且指定工作簿已设置密码,则提示用户输入密码。rnWriteResPassword 该字符串为一个写保护工作簿的写入权密码。如果省略该参数并且指定工作簿已设置密码,则提示用户输入密码。rnIgnoreReadOnlyRecommended 如果该值为True,则设置Microsoft Excel不显示建议只读消息(如果该工作簿以“建议只读”选项保存)。rnOrigin 如果该文件为文本文件,则该参数用于指示该文件来源于何种操作系统(以便正确映射代码页和回车/换行(CR/LF))。可为以下XlPlatform常量之一:xlMacintosh、xlWindows 或 xlMSDOS。如果省略本参数,则使用当前操作系统。rnDelimiter 如果该文件为文本文件并且Format参数为 6,则此参数用于指定用作分隔符的字符。例如,可使用Chr(9)代表制表符,使用“,”代表逗号,使用“;”代表分号或者使用自定义字符。如果该参数为字符串,则只使用该字符串的第一个字符。rnEditable 如果该文件为Microsoft Excel 4.0加载宏,则该参数的值为True时可打开该加载宏以便在窗口中看到。如果该参数的值为False或者省略该参数,则该加载宏以隐藏方式打开,并且无法设为可见。本选项不能应用于由Microsoft Excel 5.0或更高版本的Microsoft Excel创建的加载宏。如果该文件是Excel模板,则参数的值为True时,会打开指定模板用于编辑。参数为False时,可根据指定模板打开新的工作簿。默认值为False。rnNotify 当该文件不能以可读写模式打开时,如果该参数的值为True,则可将该文件添加到文件通知列表。Microsoft Excel将以只读模式打开该文件并轮询文件通知列表,当文件通知列表中的该文件可用时通知用户。如果该参数的值为False或省略该参数,则不请求任何通知,并且不能打开任何不可用的文件。rnConverter 打开文件时试用的第一个文件转换器的索引号。首先使用的是指定的文件转换器:如果该转换器不能识别此文件,则试用所有的转换器。转换器索引号由FileConverters属性返回的转换器行号组成。rnAddToMru 如果该值为True,则将该工作簿添加到最近使用的文件列表中。默认值为False。rnLocal 如果该值为True,则以Microsoft Excel(包括控制面版设置)的语言保存文件。如果该值为False(默认值),则以Visual Basic for Applications (VBA)的语言保存文件,其中Visual Basic for Applications (VBA)为典型安装的美国英语版本,除非VBA项目的Workbooks.Open来自旧的国际化的XL5/95VBA项目。rnCorruptLoad 可为以下常量之一:xlNormalLoad、xlRepairFile和xlExtractData。如果未指定任何值,则默认值通常为普通状态,但如果 Excel已尝试打开该文件,则可以是安全加载或数据恢复状态。首选值为普通状态。如果Excel在打开文件时停止操作,则为安全加载状态。如果 Excel再次停止操作,则为数据恢复状态。rnrn 对于Workbooks的更一步的操作会在以后的文章中继续讨论。rn 论坛

Interop开发Excel(一)

08-29

Borland公司从Delphi 8开始支持.NET的编程,也就为我们打开了用Delphi+Interop来编写Office程序的大门。rnrn 笔者曾就用BCB 6/Delphi 7进行Office开发写过若干文章,目前都放置在本站点,可以用相关TAG进行搜索(连接在此)。在这几篇早期的文章中,我也明确表示我会将开发的方向转移到利用Interop来开发Office上,那么接下来的一些时间我将兑现我的承诺,我将用BDS 2006来开发基于Interop的Office程序。rnrn 关于Interop的概念,我这里不再展开,有兴趣的读者可以自行参阅Microsoft的说明文档。在当前,我们只要知道它是我们可以用来在Dephi中开发.NET的Office程序的Assembly就可以了。rnrn 一、准备工作rnrn 一般而言,我们都会先安装Windows XP,然后安装Office,然后安装BDS。但是这样的安装将无法提供Office的PIA(Primary Interop Assembly),因此我们需要重新启动Office的安装程序,增加Office组件。rnrn 在增加Office的组件时,一定要选择自定义安装,同时在接下来弹出的对话框中要选中“选择应用的高级定制”(大意如此),于是 Office安装程序就会弹出有关Office各个组件的更详细的选项列表,从中我们展开任何一个Office基本组件的话,都可以看到类似如下的 “.NET 可编程性支持”选项:rnrn.NET支持rnrn 这就是我们要与之大打交道的Office PIA。Office PIA安装在Windows安装目录之下的Assembly目录中,在资源管理器中浏览起来大概是这样的:rnrnAssemblyrnrn 我们可以清楚的看到Microsoft名下已经有了类似Microsoft.Office.Interop.Excel这样的PIA。rnrn 二、在BDS 2006中引用PIArnrn 让我们启动BDS 2006(当然也可以只启动Delphi for .NET),并选择File|New,继续选择VCL Forms Application - Delphi for .NET。rnrnBDS New Projectrnrn BDS会创建一个空白的窗体,一个空白的单元文件。在继续下一步之前,先保存这个工程。选择菜单中的View|Project Manager观察工程管理窗口,并在References结点处右击并选择Add Reference,在弹出的对话框中切换到COM Imports页,滚动到Microsoft Excel 11.0 Object Library处:rnrnAdd Referencernrn 选择Add Reference并继续选择OK关闭对话框。在References结点下就会出现如下的新的引用:rnrnNew Referencernrn 参照上面的过程,继续添加对Microsoft Visual Basic for Aplication Extensibility和OLE Automation (2.0)的引用。编译这个工程,应该是没有错误了。rnrn 我们来看看如何简单的启动Excel应用并退出Excel应用。打开单元文件,在头部的uses段落加入Microsoft.Office.Interop.Excel。随意在窗体上放置一个按钮,并书写它的事件处理函数如下:rnprocedure TForm1.Button1Click(Sender: TObject);rnvar e: Microsoft.Office.Interop.Excel.ApplicationClass;rnbeginrne:=Microsoft.Office.Interop.Excel.ApplicationClass.Create;rne.Visible:=true;rne.Caption:='在BDS 2006中调用Excel';rnend;rnrn 编译运行,Excel可以成功的被调用并显示:rnrnExcelrnrn 当然,现在的Excel应用中没有任何实质性的东西。而这个DEMO也只是先展示一下框架而已。rnrn 如果我们终止程序的运行,可以看到Excel也会结束运行,如果此时我们打开任务管理器并观察进程列表,可以发现并没有Excel进程。这说明Excel进程“完整”的退出了。rnrn 如果我们再次回到我们保存工程的目录,可以看到除了常规的pas、nfm、bdsproj、exe、res文件之外,又多了三个dcpil 文件,它们分别是Microsoft.Office.Interop.Excel.dcpil,Microsoft.Vbe.Interop.dcpil 和stdole.dcpil,分别对应了我们在工程中引用的三个COM类型库。rnrn 在BDS中,不仅有Delphi .NET,也有C#。我不知道对于原先BCB的忠实拥趸者来说,是更愿意转移到Delphi还是更愿意转移到C#,或者更愿意选择等待 C++Builder .NET的出现?为了不偏不倚,我们下面给出C#的代码,同样在BDS 2006环境中调试完成:rnrnpublic class WinForm : System.Windows.Forms.Formrnrn/// rn/// Required designer variable.rn/// rnprivate System.ComponentModel.Container components = null;rnprivate System.Windows.Forms.Button button1;rnprivate Microsoft.Office.Interop.Excel.Application EA;rnrnpublic WinForm()rnrn//rn// Required for Windows Form Designer supportrn//rnInitializeComponent();rnrn//rn// TODO: Add any constructor code after InitializeComponent callrn//rnrnrn/// rn/// Clean up any resources being used.rn/// rnprotected override void Dispose(bool disposing)rnrnif (disposing)rnrnif (components != null)rnrncomponents.Dispose();rnrnrnbase.Dispose(disposing);rnrnrn#region Windows Form Designer generated codern/// rn/// Required method for Designer support - do not modifyrn/// the contents of this method with the code editor.rn/// rnprivate void InitializeComponent()rnrnthis.button1 = new System.Windows.Forms.Button();rnthis.SuspendLayout();rn//rn// button1rn//rnthis.button1.Location = new System.Drawing.Point(16, 264);rnthis.button1.Name = "button1";rnthis.button1.TabIndex = 0;rnthis.button1.Text = "button1";rnthis.button1.Click += new System.EventHandler(this.button1_Click);rn//rn// WinFormrn//rnthis.AutoScaleBaseSize = new System.Drawing.Size(6, 14);rnthis.ClientSize = new System.Drawing.Size(464, 310);rnthis.Controls.Add(this.button1);rnthis.Name = "WinForm";rnthis.Text = "WinForm";rnthis.ResumeLayout(false);rnrn#endregionrnrn/// rn/// The main entry point for the application.rn/// rn[STAThread]rnstatic void Main()rnrnSystem.Windows.Forms.Application.Run(new WinForm());rnrnrnprivate void button1_Click(object sender, System.EventArgs e)rnrnbutton1.Text="Clicked";rnobject m=Type.Missing;rnEA=new Microsoft.Office.Interop.Excel.Application();rnrnEA.Visible=true;rnEA.Caption="Invoke Excel from C#";rnrnrnrnrn 之所以把这一长串代码都贴出来,是因为有一个特别的地方需要指出,也就是上面代码段中的 System.Windows.Forms.Application.Run(new WinForm());。由于Excel命名空间也有一个Application(也就是Excel Application),所以我们只能在显示主窗体的时候用上完整的名称空间,否则编译无法通过。但是如果你的BDS打过了UPDATE 1的补丁,那么这个问题就不再存在。rnrn 从代码的比较我们也可以看出,C#和Delphi .NET在通过Interop操作Office时,过程、代码都惊人的相似。这给我们也带来了巨大的好处。rnrn 在以后的文章中,我会先从Excel开始详细讨论一些具体的操作,然后是Word,Outlook,最后是一些其他的程序。之所以这样考虑,是因为Microsoft本身对Interop操作的说明也集中在Excel/Work/Outlook上。我只是想保持我的文章与MSDN文档的一致性而已。rn 论坛

Interop开发Excel(四)

08-29

笔者曾经有若干篇文章介绍了在BDS 2006下开发Interop程序的一般步骤。rnrn 上面提到的开发方法,是建立一个程序,然后创建Excel的应用对象(Application),再获得相应接口(如Workbook,Worksheet,Cells)的引用并对其进行进一步的操作。调用方式是通过由外(自己开发的程序)到内(Excel)。rnrn 今天我要介绍一种新的利用.NET开发Interop应用的方法。这种方法与上述方法最大的不同就在于它是由内(Excel)到外(自己开发的程序)进行的。rnrn 一、开发工具和准备工作:rnrn 这次我使用的是免费的C#开发工具:SharpDeveloper。大家可以免费下载,免费使用。操作系统是Vista Business,但是XP平台应该一样可用。Office平台是2003.rnrn 同样的,要使用Office的Interop PIA,必须在安装时(或在安装后)选择各个Office应用(Word/Excel/...)的.NET编程支持,详细的步骤见前文描述,这里不再重复。rnrn 二、建立Excel加载项rnrn 启动#Developer后,选择File|New|Solution,在弹出窗口中选择C#模板中的Class Library。设置项目的位置、名称后单击“Create”,即可。rnrn创建DLLrnrn #Developer会自动创建程序的项目文件,这时界面如下:rnrn编程界面rnrn 我们首先要在左侧的Solution Explorer中的References节点下加入对Excel PIA的引用。右击References节点,并选择Add Reference,在弹出窗口中选择GAC中的Microsoft.Office.Interop.Excel如下:rnrn添加PIA引用rnrn 注意,必须使用GAC中的类!而不是COM中的类!rnrn 我们对IDE自动生成的MyClass.cs文件要做一些修改,增加两行代码(以粗体标出):rnrnusing System;rnusing System.Collections.Generic;rnusing Excel=Microsoft.Office.Interop.Excel;rnrn[assembly: System.ComponentModel.DescriptionAttribute("OfficeStartupClass, Version=1.0, Class=TestOLE.MyClass")]rnrn 其中的using子句是常规的。而特别重要的,而且是必须要加的是[assembly: ...]这一行。用户可能需要修改的是Class=TestOLE.MyClass。在我的应用中,我创建的DLL的namespace是 TestOLE,而其中的类是MyClass。rnrn 在MyClass类中,需要创建两个Excel调用的特定函数:_Startup/_Shutdown。代码如下:rnpublic void _Startup(object thisApp, object thisWorkbook)rnrnthis.MyApp=thisApp as Excel.Application;rnthis.MyWorkBook=thisWorkbook as Excel.Workbook;rnthis.MyWorksheet=MyWorkBook.Worksheets[1] as Excel.Worksheet;rnrnMyWorksheet.Cells[1,1]="The add-on starts!";rnrnrnpublic void _Shutdown()rnrnMyApp=null;rnMyWorkBook=null;rnMyWorksheet=null;rnrnrn 其中的MyApp/MyWorkBook/MyWorksheet是一些自己定义的变量。此时我们可以编译该程序,生成MyClass.dll了。rnrn 三、安全因素rnrn 值得注意的是,即使我们是在.NET 2.0/3.0下开发我们的DLL,但是这个DLL的安全性需要在.NET 1.x下进行配置。有关.NET 1.x的安全性配置,可以参考其它文档,这里不再赘述。rnrn 四、与Excel的工作簿链接rnrn 现在我们的程序虽然已经编译完毕,但是还没有和一个Excel工作簿关联。要做到这一步,需要创建(或者打开)一个工作簿,对该工作簿做一些修改。在菜单中选择“文件|属性”,在弹出的对话框中选择“自定义”:rnrn自定义属性rnrn 我们需要加入两个自定义属性:_AssemblyLocation0和_AssemblyName0。第一个属性设置为我们的DLL所在的位置,在我的应用中是D:\Go4Pro Programs\00017\bin\Debug,而第二个属性是我们的DLL的名称,我的是TestOLE。rnrn 保存这个文件,然后重新打开,可以看到在A1这个单元格中显示:“The add-on starts!”字样。这证明我们编写的DLL已经被该工作簿加载并执行。rnrn 五、交互rnrn 只有这样的功能显然不是我们想要的。我们需要加入一个交互的过程,让用户最终控制工作簿的行为。在这里我选择修改工作簿的标题作为演示。rnrn 首先我们在MyClass.cs中创建两个方法,一个消息响应:rnrnprivate Excel.WorkbookEvents_SheetFollowHyperlinkEventHandler followHyperlinksEvent;rnrnpublic void _Startup(object thisApp, object thisWorkbook)rnrnthis.MyApp=thisApp as Excel.Application;rnthis.MyWorkBook=thisWorkbook as Excel.Workbook;rnthis.MyWorksheet=MyWorkBook.Worksheets[1] as Excel.Worksheet;rnrnMyWorksheet.Cells[1,1]="The add-on starts!";rnfollowHyperlinksEvent=new Excel.WorkbookEvents_SheetFollowHyperlinkEventHandler(ThisWorkbook_SheetFollowHyperlink);rnMyWorkBook.SheetFollowHyperlink+=followHyperlinksEvent;rnrnrnprotected void ThisWorkbook_SheetFollowHyperlink(System.Object s, Excel.Hyperlink l)rnrnswitch (l.SubAddress)rnrncase "ChangeTitle":rnChangeTitle();rnbreak;rndefault:rnbreak;rnrnrnrnpublic void ChangeTitle()rnrnMyApp.Caption="Invoked from Excel (supported by C#)";rnrnrn 在ThisWorkbook_SheetFollowHyperlink中,我们可以看到我们采用的是超链接的方式来激活相应的方法。这就还需要我们在Excel工作簿中进行一些工作。rnrn 在我们刚才的工作簿中,随便选择一个单元格,输入一些内容,诸如“Click here to change the title!”。然后将该单元格命名为ChangeTitle(这个名字必须和上面代码中case段的内容一致)。然后在该单元格选中的情况下,选择菜单 “插入|超链接”:rnrn建立超链接rnrn 选择“本文档中的位置”中的“定义的名称”中的ChangeTitle即可。保存Excel文件并再次打开,点击“Click here to change the title!”单元格,我们可以看到工作簿的名称得以改变:rnrn成功!rnrn 六、与以前方法的比较rnrn 显然,这个方法比以前的方法更加灵活而友好,因为用户将在一个界面中进行操作,而不用在两个界面中进行切换。而且只要关闭工作簿就可以结束DLL,不用再关闭另一个主程序。rnrn 但是,这个方法需要我们开启.NET 1.x的安全性,需要一些额外的设置,并不一定是每个终端用户都能胜任的。rnrn 孰优孰劣,留待开发者和用户进行判断。rnrn rn 论坛

COM Interop 闹鬼

11-17

通过使用 COM Interop 实用工具 (TlbImp.exe) 导入相关的 COM 类型成功地把VB6.0中最常用的串口控件引入到了C#工程中,编写串口程序方便了许多.rn 这次编写实时数据库系统PHD接口时,以同样的方法把 PHData.tlb 亦成功导入.rn 在new 一个具体的类实例时报错:(0x80040154) COM object with CLSIS XXXX IS either not valid or not registered.rn 显然是说COM组件没注册好,可是问题是 rn PHData已经注册成功(regsvr32) -- 由系统厂商提供的安装包完成.rn 从PHData导出的四个类,new 第一个时候并没有报错,测试代码连接服务器成功rn new 其他三个皆报错rn-----------------------------------------------rn PHData.tlb导出类型列表:rn CPHDataClassrn CTagsClassrn CTagClassrn IPHDTagDefinitionClassrn----------------------------------------------- rn 应用代码:rn PHData.CPHDataClass m_PHData = new PHData.CPHDataClass();rn m_PHData.HostName = "localhost";rn m_PHData.Login("UserName","Password");rn //####################rn 以下代码运行时出错rn PHData.CTagsClass tags = new PHData.CTagsClass();rn PHData.CTagClass tag = new PHData.CTagClass();rn ... ... rn------------------------------------------------rn第一个类CPHDataClass对应的COM (PHData.tlb)已经注册rn其他的类也是从PHData.tlb导出的:rn 系统竟然没有注册? 如何注册?rn 还是TlbImp工具有问题? 还是需要什么特殊参数?rn CPHDataClass的一个属性的类型是CTagsClass.而tlbimp将其改为Object类型.是否有关?rnrn恳请对COM原理和.NET COM Interop 熟悉的大侠指点迷津. 论坛

没有更多推荐了,返回首页