如何在python

时间:2016-01-20 15:30:20

标签: c# python debugging onenote onenote-api

为了从日志记录脚本收集输出,我想使用onepy向OneNote 2013笔记本添加信息。不幸的是,onepy提供的方法update_page_content()对我不起作用。为了更深入地理解这个问题,我切换到了C#,其中存在许多OneNote API的在线示例,在一些trouble之后,我设法让以下简约的C#示例工作:

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代码之间的区别?

2 个答案:

答案 0 :(得分: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

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

    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)

显然,python 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 - &gt; Int - &gt; 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想要的。叹。为什么关于如何存储日期有这么多不同的协议?哦,只是提一下。 Office for Mac使用1904作为零点。是的,当然。

答案 1 :(得分: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才会继续更新。传递此参数的值有助于防止意外覆盖自上次修改页面以来用户所做的编辑。