2016-01-20 173 views
2

为了从日志记录脚本中收集输出,我想使用onepy将信息添加到OneNote 2013笔记本中。不幸的是,由onepy提供的方法update_page_content()不适用于我。为了让问题更深入的了解,我切换到C#,其中用于OneNote的API网上很多例子表明,有些trouble后,我设法得到以下简约的C#示例工作:如何在Python中调试win32com调用

using System; 
using OneNote = Microsoft.Office.Interop.OneNote; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     OneNote.Application onenoteApp = new OneNote.Application(); 
     string xml = "<one:Page xmlns:one=\"http://schemas.microsoft.com/office/onenote/2013/onenote\" ...> ... </one:Page>"; 
     onenoteApp.UpdatePageContent(xml, DateTime.MinValue); 
    } 
} 

字符串xml是通过修改XML文档获得的,该文档是使用GetPageContent方法从OneNote中检索的,详见我链接的上一个问题。 xml的确切内容对于这个问题无关紧要,唯一重要的是上述程序一次又一次没有问题地运行,并且对现有OneNote页面的更改始终成功执行。

现在转到Python,我试图翻译我的简约程序而没有做出实质性的改变。我的结果如下所示:

import win32com 
import pytz 
import datetime 

onenote_app = win32com.client.Dispatch('OneNote.Application.15') 
xml = "<one:Page xmlns:one=\"http://schemas.microsoft.com/office/onenote/2013/onenote\" ...> ... </one:Page>" 
date = pytz.utc.localize(datetime.datetime.fromordinal(1)) 
onenote_app.UpdatePageContent(xml, date) 

我试图非常小心地使用两个变量的相同值。当然这两个字符串xml的内容是相同的(复制&粘贴)。另外,根据VS2015调试器,DateTime.MinValuedate指的是同一日期。然而,当我执行python程序时,我收到了这个非常无益的错误。

135   def UpdatePageContent(self, bstrPageChangesXmlIn=defaultNamedNotOptArg, dateExpectedLastModified=(1899, 12, 30, 0, 0, 0, 5, 364, 0), xsSchema=2, force=False): 
    136   return self._oleobj_.InvokeTypes(1610743816, LCID, 1, (24, 0), ((8, 1), (7, 49), (3, 49), (11, 49)),bstrPageChangesXmlIn 
--> 137    , dateExpectedLastModified, xsSchema, force) 
    138 
    139  _prop_map_get_ = { 

com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2147213296), None) 

据我了解,在C#和Python的实际使用相同的库来执行他们的电话(包括所谓的Microsoft OneNote 15.0 Object Library)。所以原则上这两个程序都应该正常工作。如果我在这一点上没有弄错的话,那么我会假定Python在的调用中不同于。我怎样才能进一步追溯这里的实际问题?有没有办法使用Visual Studio 2015的内置Python支持来更好地理解C#和Python代码之间的区别?

回答

1

由于jayongg已经在他的answer中指出,我的问题是作为参数传递给UpdatePageContent()调用的日期。然而,他建议通过零作为最后修改日期并且禁用日期检查,如documentation中所详述的那样,并不像看起来那么直截了当。在我的回答中,我将详细介绍我遇到的所有陷阱。

第一个问题显示在this问题中。如果试图将例如datetime对象或纯整数传递给UpdatePageContent(),则会引发如下错误。

135   def UpdatePageContent(self, bstrPageChangesXmlIn=defaultNamedNotOptArg, dateExpectedLastModified=(1899, 12, 30, 0, 0, 0, 5, 364, 0), xsSchema=2, force=False): 
    136   return self._oleobj_.InvokeTypes(1610743816, LCID, 1, (24, 0), ((8, 1), (7, 49), (3, 49), (11, 49)),bstrPageChangesXmlIn 
--> 137    , dateExpectedLastModified, xsSchema, force) 
    138 
    139  _prop_map_get_ = { 

ValueError: astimezone() cannot be applied to a naive datetime 

特别是,离开日期参数留空并使用默认值,不能按预期工作:

135   def UpdatePageContent(self, bstrPageChangesXmlIn=defaultNamedNotOptArg, dateExpectedLastModified=(1899, 12, 30, 0, 0, 0, 5, 364, 0), xsSchema=2, force=False): 
    136   return self._oleobj_.InvokeTypes(1610743816, LCID, 1, (24, 0), ((8, 1), (7, 49), (3, 49), (11, 49)),bstrPageChangesXmlIn 
--> 137    , dateExpectedLastModified, xsSchema, force) 
    138 
    139  _prop_map_get_ = { 

TypeError: must be a pywintypes time object (got tuple) 

显然蟒蛇API调用的一些内部的混乱。如果按照onepy project page上的说明进行操作并使用makepy预先生成API封装器,那么可以检查负责这些调用的源文件,但至少对我而言这并不是很有启发性。我怀疑InvokeTypes方法试图将传递日期转换为API可以理解的格式,但是必要的需求并未在Python包装器本身中实现。

幸运的是,有一个相当简单的解决方法。诀窍是通过datetime对象时区感知。为此,必须先将其本地化。这可以通过使用pytz模块来完成。

import datetime 
import pytz 

date = pytz.utc.localize(datetime.datetime.fromordinal(1)) 

有了这些知识,API调用就可以工作,但会引发COM错误。这是jayongg的答案:我必须将最后修改日期(或者我需要知道日期,最后一次修改发生的时间,也应该有效)通过零。现在棘手的问题是:什么是零?

在我的C#代码中,此日期由DateTime.MinValue给出,根据Visual Studio 2015等于0001-01-01 00:00:00。 Python中的同一日期是datetime.datetime.fromordinal(1),但它仍然不能使呼叫工作。我不得不怀疑Visual Studio提供的信息是错误的,或者发生了一些奇怪的事情,也许是类型转换VSDate - > Int - > APIDate。

那么我怎么知道哪一个是正确的零日期?原来,答案已经存在,只需要知道在哪里寻找。如果一个在Python API的包装检查,存在的问题为给定参数的默认值:

dateExpectedLastModified=(1899, 12, 30, 0, 0, 0, 5, 364, 0) 

同样可以用下面的代码段而获得。

>> date = pytz.utc.localize(datetime.datetime(year=1899, month=12, day=30)) 
>> print(tuple(date.timetuple())) 
(1899, 12, 30, 0, 0, 0, 5, 364, 0) 

而且,将可变日期传递给调用工作得很好。

>> onenote_app.UpdatePageContent(xml, date) 
>> 

完美无缺,对不对?我的意思是,你会通过哪个日期而不是1899-12-30?其实这是有道理的。这一天是Dublin Julian Date的零点前一天。根据德语Wikipedia article,Excel将这一天用作日期的零点,因此OneNote的做法听起来似乎是合理的。

为什么有一天的区别?显然,1900年被误认为闰年,事实并非如此。从而1899-12-31转移到了1899-12-30,这正是API所要求的。叹。为什么有如此多的关于如何存储日期的不同协议?哦,只是提及。 Mac版Office使用1904作为零点。嗯,当然咯。

0

你的错误代码(-2147213296)为0x80042010,这就是:

hrLastModifiedDateDidNotMatch

0x80042010

的最后修改日期不符。

https://msdn.microsoft.com/en-us/library/office/ff966472(v=office.14).aspx

您可以尝试通过最后修改日期为0 来源:https://msdn.microsoft.com/en-us/library/office/gg649853(v=office.14).aspx: dateExpectedLastModified-(可选),你认为你需要更新上次修改网页的日期和时间。 如果您为此参数传递非零值,则只有在您传递的值与页面上次修改的实际日期和时间匹配时,OneNote才会继续更新。传递此参数的值有助于防止意外覆盖自上次修改页面以来对用户所做的编辑。

+0

你说得对,怀疑日期过了就是问题所在。与此同时,我得到了python程序的运行,并且一旦找到时间,我会尽快添加我的解决方案。但是,你不回答我的更一般的问题:你怎么知道-2147213296意味着0x80042010?这两个数字似乎与我无关。至少将-2147213296转换为十六进制给了我0x7ffbdff0,这看起来完全不相关。 – ranguwud

+0

其实它给你-0x7ffbdff0(负数)。您需要将其转换为正面。你可以通过向结果添加0x100000000来完成。 – jayongg

+0

这是真的,但对我来说显得任意。例如,我也可以添加0xffffffff并获得不同的结果。添加0x100000000这是一个常规规则,它总是** true?另外,为什么没有增加正面的返回值? – ranguwud