VisualStudio如何进行OneNote插件开发?

文章目录

  • 0、引言
  • 1、工具和数据准备
  • 2、创建工程
  • 3、创建ribbon配置文件
  • 4、编写功能代码
  • 5、安装和部署
  • 6、OneNote插件展示

0、引言

  在OneNote做笔记很方便,但笔者用久后,就觉得OneNote缺少自己想要的一些功能,希望通过二次开发实现不断增长的需求。起初对OneNote二次开发是利用Onetastic通过宏实现较为简单的二次开发,但用久后,还是不满足日益增长的需求。偶然在网上找到一篇关于利用VisualStudio进行OneNote二次开发的文章[2],本文以笔者的电脑系统环境重现了该文章的方法,并补充了该文章开发过程中不详细的内容,得到了想要的二次开发的结果。

1、工具和数据准备

  (1)工具准备;
  本文使用VisualStudio2015(VS2015)进行OneNote2016的插件开发,VS2015没有自带的安装和部署工具,需要自行安装部署软件:Microsoft Visual Studio 2015 Installer Projects(下载地址: https://marketplace.visualstudio.com/items?itemName=visualstudioclient.MicrosoftVisualStudio2015InstallerProjects)。
  (2)数据准备。
  本文涉及一张用于插件显示的图片(HelloOneNote.png),如下所示:
  在这里插入图片描述

2、创建工程

  (1)创建类库;
  在这里插入图片描述
  (2)勾选使程序集COM可见;
  在这里插入图片描述
  (3)勾选为COM互操作注册。
  在这里插入图片描述

3、创建ribbon配置文件

  (1)添加ribbon.xml文件;
  在这里插入图片描述
  (2)将配置文件存入工程资源;
  在这里插入图片描述
  (3) 打开ribbon.xml文件,编写配置文件代码。
  在这里插入图片描述

<?xml version="1.0" encoding="utf-8" ?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" loadImage="GetImage">
  <ribbon>
    <tabs>
      <tab id="tabCustom" label="Custom">
        <group id="groupHello" label="Hello">
          <button id="buttonHello" label="HelloOneNote" size="large" screentip="Press this for a 'Hello World!' message" onAction="showHello" image="HelloOneNote.png" />
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

4、编写功能代码

  (1)为工程添加引用:Extensibility、office、Microsoft OneNote 15.0 Object Library、System.Windows.Forms和System.Drawing;
  在这里插入图片描述
  详细添加步骤:
  在这里插入图片描述
  ①添加Extensibility;
  在这里插入图片描述
  ②添加office;
  在这里插入图片描述
  ③添加Microsoft OneNote 15.0 Object Library;
  在这里插入图片描述
  ④添加System.Windows.Forms;
  在这里插入图片描述
  ⑤添加System.Drawing。
  在这里插入图片描述
  (2)在Class1中添加using;

using System;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using System.Xml.Linq;
using Extensibility;
using Microsoft.Office.Core;
using OneNote = Microsoft.Office.Interop.OneNote;

  (3)创建GUID;
  在这里插入图片描述
  本文GUID:{AD9144B5-6B8F-4C5D-8B5C-3A5E5C1C0FA1}
  (4)在Class1上添加标记,并继承接口;
  在这里插入图片描述

[Guid("AD9144B5-6B8F-4C5D-8B5C-3A5E5C1C0FA1"), ProgId("HelloOneNote.Class1")]
public class Class1 : IDTExtensibility2, IRibbonExtensibility
{

}

  (5)在Class1中添加以下方法(实现接口IDTExtensibility2、实现接口IRibbonExtensibility);
  在这里插入图片描述

private OneNote.Application onApp = new OneNote.Application();
private object application;
public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
{
    application = Application;
}
public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)
{
    application = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();
}
public void OnAddInsUpdate(ref Array custom) { }
public void OnStartupComplete(ref Array custom) { }
public void OnBeginShutdown(ref Array custom)
{
    if (application != null)
    {
        application = null;
    }
}
public string GetCustomUI(string RibbonID)
{
    return Properties.Resources.ribbon;
}

  注:以上属性和方法内容一般不用更改。
  (6)在Class1中添加以下方法(实现ribbon.xml中按钮的onAction事件调用的函数);
  在这里插入图片描述

public void showHello(IRibbonControl control)
{
    var app = application as OneNote.Application;
    var win = app.Windows;
    string id = (application as OneNote.Application).Windows.CurrentWindow.CurrentPageId;
    string title;
    app.GetPageContent(id, out title);
    var doc = XDocument.Parse(title);
    string pageTitle = doc.Descendants().FirstOrDefault().Attribute("ID").NextAttribute.Value;
    MessageBox.Show("Current Page ID = " + pageTitle, "Hello OneNote,I am cacrle.");
}

  (7)添加按钮图片。
  ①将HelloOneNote.png图片拖进资源中;
  在这里插入图片描述
  ②在Class1中添加以下方法(实现ribbon.xml中加载图片的函数)。
  在这里插入图片描述

public IStream GetImage(string imageName)
{
    MemoryStream mem = new MemoryStream();
    Properties.Resources.HelloOneNote.Save(mem, ImageFormat.Png);
    return new CCOMStreamWrapper(mem);
}

class CCOMStreamWrapper : IStream
{
    public CCOMStreamWrapper(System.IO.Stream streamWrap)
    {
        m_stream = streamWrap;
    }

    public void Clone(out IStream ppstm)
    {
        ppstm = new CCOMStreamWrapper(m_stream);
    }

    public void Commit(int grfCommitFlags)
    {
        m_stream.Flush();
    }

    public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
    {
    }

    public void LockRegion(long libOffset, long cb, int dwLockType)
    {
        throw new System.NotImplementedException();
    }

    public void Read(byte[] pv, int cb, IntPtr pcbRead)
    {
        Marshal.WriteInt64(pcbRead, m_stream.Read(pv, 0, cb));
    }

    public void Revert()
    {
        throw new System.NotImplementedException();
    }

    public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
    {
        long posMoveTo = 0;
        Marshal.WriteInt64(plibNewPosition, m_stream.Position);
        switch (dwOrigin)
        {
            case 0:
                {
                    /* STREAM_SEEK_SET */
                    posMoveTo = dlibMove;
                }
                break;
            case 1:
                {
                    /* STREAM_SEEK_CUR */
                    posMoveTo = m_stream.Position + dlibMove;

                }
                break;
            case 2:
                {
                    /* STREAM_SEEK_END */
                    posMoveTo = m_stream.Length + dlibMove;
                }
                break;
            default:
                return;
        }
        if (posMoveTo >= 0 && posMoveTo < m_stream.Length)
        {
            m_stream.Position = posMoveTo;
            Marshal.WriteInt64(plibNewPosition, m_stream.Position);
        }
    }

    public void SetSize(long libNewSize)
    {
        m_stream.SetLength(libNewSize);
    }

    public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
    {
        pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
        pstatstg.cbSize = m_stream.Length;
        if ((grfStatFlag & 0x0001/* STATFLAG_NONAME */) != 0)
            return;
        pstatstg.pwcsName = m_stream.ToString();
    }

    public void UnlockRegion(long libOffset, long cb, int dwLockType)
    {
        throw new System.NotImplementedException();
    }

    public void Write(byte[] pv, int cb, IntPtr pcbWritten)
    {
        Marshal.WriteInt64(pcbWritten, 0);
        m_stream.Write(pv, 0, cb);
        Marshal.WriteInt64(pcbWritten, cb);
    }

    private System.IO.Stream m_stream;
}

5、安装和部署

  (1)在解决方案中添加安装和部署的项目;
  在这里插入图片描述
  (2)修改注册表;
  在这里插入图片描述
  ①HKEY_CLASSES_ROOT→AppID→{工程的GUID}:新建字符串值,Name:DllSurogate,Value为空;
  在这里插入图片描述
  ②HKEY_CLASSES_ROOT→CLSID→{工程的GUID}:新建字符串值,Name:AppID,Value:{工程的GUID};
  在这里插入图片描述
  ③HKEY_CURRENT_USER→Software→Microsoft→Office→OneNote→AddIns→工程的ProgId;
  工程的ProgId,一般为“命名空间.类名"(本为HelloOneNote.Class1)
  添加3个键:
  字符串值:Name:Description,Value:自定义的工程描述
  字符串值:Name:FriendlyName,Value:自定义的工程名称(本文为ShowFirstOneNotePlugin)
  DWORD值:Name:LoadBehavior,Value:3
  在这里插入图片描述
  ④HKEY_LOCAL_MACHINE→Software→Classes→AppID→{工程的GUID}:新建字符串值,Name:DllSurrogate,Value为空;
  在这里插入图片描述
  ⑤ HKEY_LOCAL_MACHINE→Software→Classes→CLSID→{工程的GUID}:新建字符串值,Name:AppID,Value:{工程的GUID}。
  在这里插入图片描述
  (3)设置项目输出;
  在这里插入图片描述
  (4)生成解决方案;
  在这里插入图片描述
  (5)安装。
  ①通过VS安装;
  在这里插入图片描述
  ②通过安装文件安装(可共享该文件)。
  在这里插入图片描述
  注:安装过程一般保持默认参数,一直下一步即可。

6、OneNote插件展示

  在这里插入图片描述

参考资料:
[1] cacrle. VisualStudio如何进行桌面软件开发?; 2023-03-18 [accessed 2023-04-06].
[2] johnhuang. 创建属于自己的OneNote插件; 2017-01-20 [accessed 2023-04-06].
[3] 微软官网. How to develop an OneNote 2010 ribbon add-in application; [accessed 2023-04-06].
[4] 微软官网. Deploying a Visual Studio 2010 Tools for Office Solution Using; [accessed 2023-04-06].

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/7979.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springcloud:xxl-job的任务触发机制及调度过期策略

0.引言 我们都会用xxl-job&#xff0c;但很少有人能够说清楚xxl-job的任务触发机制&#xff0c;面临任务阻塞、服务重启如何处理任务&#xff0c;本期我们就来一起看看xxl-job的任务触发机制 1. 调度过期策略 我们在配置策略时可以看到有一个调度过期策略配置&#xff0c;也…

IT知识百科:什么是BGP?

BGP&#xff08;Border Gateway Protocol&#xff09;是一种用于在互联网中交换路由信息的协议。BGP在互联网的路由选择和转发中扮演着至关重要的角色&#xff0c;是互联网的核心协议之一。在本文中&#xff0c;我们将深入探讨BGP的基本概念、特点和使用方法。 一、BGP的基本概…

Linux系统【centos7】常用系统命令大全

CentOS 7是一款流行的Linux操作系统。在本教程中&#xff0c;我们将介绍CentOS 7的一些重要功能和基本操作。 安装CentOS 7 1. 下载CentOS 7 ISO文件&#xff0c;并将其刻录到DVD上。 2. 将DVD插入计算机并启动。选择从DVD启动&#xff0c;进入CentOS 7安装程序。 3. 选择语…

剪枝与重参第二课:修剪方法和稀疏训练

目录修剪方法和稀疏训练前言1.修剪方法1.1 经典框架&#xff1a;训练-剪枝-微调1.2 训练时剪枝(rewind)1.3 removing剪枝2.dropout and dropconnect3.稀疏训练(Sparse training)总结修剪方法和稀疏训练 前言 手写AI推出的全新模型剪枝与重参课程。记录下个人学习笔记&#xff…

webpack5搭建react框架-开发环境配置

webpack5配置react基础开发环境 1、前言 之前已经使用webpack5进行了react框架基础环境的搭建&#xff08;基础环境配置&#xff09;&#xff0c;但是每次修改完项目代码都需要重新执行npm run build指令进行构建&#xff0c;并且需要刷新浏览器中的页面查看最新修改的代码结…

【Linux】文件系统

文章目录文件系统1. 了解磁盘的物理结构2. 磁盘的具体物理存储结构3. 逻辑抽象4. 文件系统5. 软硬连接5.1 制作软硬连接&#xff0c;对比差别文件系统 我们目前的文件都是被打开的&#xff0c;如果没有被打开那&#xff0c;在哪里&#xff1f; 一定不是在内存当中&#xff0c;只…

《论文阅读》Unified Named Entity Recognition as Word-Word Relation Classification

总结 将NER视作是word-word间的 Relation Classification。 这个word-word 间的工作就很像是TPlinker那个工作&#xff0c;那篇工作是使用token间的 link。推荐指数&#xff1a;★★★☆☆值得学习的点&#xff1a; &#xff08;1&#xff09;用关系抽取的方法做NER抽取 &…

论文阅读_MAE

论文信息 name_en: Masked Autoencoders Are Scalable Vision Learners name_ch: 带遮蔽的自编码器是大规模的视觉学习者 paper_addr: https://ieeexplore.ieee.org/document/9879206/ doi: 10.1109/CVPR52688.2022.01553 date_read: 2023-04-08 date_publish: 2022-06-01 tag…

快速分析一个行业,这个工具必不可少

今天主要介绍一个做细分市场选择的工具:战略定位分析SPAN矩阵。 SPAN属于细分市场的分类排序模型。 如果用一句话来介绍这个工具,那就是: 面对纷繁的选项,我们究竟该如何做选择? 小到每天中午吃什么饭,大到生死存亡,我们每天都在做着各种各样的选择。 说到吃饭,你…

Nuxt3中的常用seo标签

title标签&#xff1a;主要是为了告诉搜索引擎我们的网站标题是什么&#xff0c;然后搜索引擎才会根据你提供的的title给你打上tag&#xff0c;用户在搜索的时候才会搜索到你。meta标签&#xff1a;这个标签根据name的不同有很多中&#xff0c;和SEO相关的主要是namedescriptio…

HuggingGPT:一个ChatGPT控制所有AI模型,自动帮人完成AI任务

最强组合&#xff1a;HuggingFaceChatGPT ——HuggingGPT&#xff0c;它来了&#xff01; 只要给定一个AI任务&#xff0c;例如“下面这张图片里有什么动物&#xff0c;每种有几只”。 它就能帮你自动分析需要哪些AI模型&#xff0c;然后直接去调用HuggingFace上的相应模型&a…

Python 02 数据类型(04元组)

一、元组 元组和列表的唯一不同&#xff1a;不能直接对元组的元素进行修改&#xff0c;删除&#xff0c;添加。 不能修改 1.1 创建元组 1.1.1 创建一个空元组 touple1() # ‘() 里面没有元素&#xff0c;表示为空元组 1.1.2 元组可以容纳任意数据类型的数据的有序集合&…

CoppeliaSim安装教程(以前叫V-REP)

最近使用机器人仿真器CoppeliaSim做一些机械臂相关的东西&#xff0c;记录一下安装过程。 注意&#xff1a;CoppeliaSim使用广泛&#xff0c;我感觉最经典的一篇文章就是andy zeng等人使用CoppeliaSim仿真器和强化学习方法进行机械臂push and grasp的研究&#xff0c;参考文献在…

【李宏毅】深度学习——HW4-Speaker Identification

Speaker Identification 1.Goal 根据给定的语音内容&#xff0c;识别出说话者是谁 2.Data formats 2.1data directory 目录下有三个json文件和很多pt文件&#xff0c;三个json文件作用标注在下图中&#xff0c;pt文件就是语音内容。 mapping文件 metadata文件 n_mels:Th…

优思学院|《精益思想》让企业快速应对市场变化的利器

1985年&#xff0c;美国麻省理工学院的丹尼尔T琼斯&#xff08;Daniel T. Jones&#xff09;、詹姆斯沃默克&#xff08;James P. Womack&#xff09;等筹资500万美元&#xff0c;用了近5年的时间对90多家汽车厂进行对比分析&#xff0c;于1990年出版了《改变世界的机器》一书&…

贵金属技术分析的止损保护

前面说过我们这些小散户&#xff0c;最多也不过十几万或者几万美金的账户&#xff0c;没有必要想国际的一些大基金那样&#xff0c;又锁仓&#xff0c;又对冲什么的&#xff0c;我们资金小的投资者&#xff0c;足够灵活&#xff0c;自然有我们存活的方法。所以我们要注意发挥我…

算法题回顾:双指针链表系列集锦

1&#xff0c;合并两个有序链表 思路 创建一个指向空的新链表&#xff0c;用来存储合并后的链表&#xff0c;p指针指向该链表。创建双指针&#xff0c;分辨指向两个链表&#xff0c;用p1, p2表示while循环&#xff0c;依次判断两个指针指向数据的大小&#xff0c;将最小值赋值…

从零开始实现一个C++高性能服务器框架----日志模块

此项目是根据sylar框架实现&#xff0c;是从零开始重写sylar&#xff0c;也是对sylar丰富与完善 项目地址&#xff1a;https://gitee.com/lzhiqiang1999/server-framework 简介 项目介绍&#xff1a;实现了一个基于协程的服务器框架&#xff0c;支持多线程、多协程协同调度&am…

Vue3走马灯(Carousel)

Vue2走马灯&#xff08;Carousel&#xff09; 可自定义设置以下属性&#xff1a; 走马灯图片数组&#xff08;imageData&#xff09;&#xff0c;类型&#xff1a;Array<{title: string, link?: string, imgUrl: string}>&#xff0c;默认 [] 自动滑动轮播间隔&#…

3-ELK+Kafka+Filebeat 海量级日志收集 TB PB级别

ELKKafkaFilebeat 终极版 4、Kafka&#xff1a; 数据缓冲队列(消息队列)。同时提高了可扩展性。具有峰值处理能力&#xff0c;使用消息队列能够使关键组件顶住突发的访问压力&#xff0c;而不会因为突发的超负荷的请求而完全崩溃。是一个分布式、支持分区的&#xff08;partit…
最新文章