用C#为Notepad++写插件:完善IDE调试体验、使用共享项目、实现窗口停靠

作者:V君 发布于:2019-1-5 21:57 Saturday 分类:折腾手记

TL;DR

完善IDE调试体验

1) 在 Notepad++ 插件目录创建文件符号链接,指向 bin\debug\x86(或x64)\ 里的 dll
2) 将项目属性-调试选项卡-启动操作的外部进程指定为 Notepad++ 的绝对路径
3) 设置断点,F5!

使用共享项目

1) 新建共享项目,命名 Kbg.NppPluginNET.PluginInfrastructure 这样可以一致命名空间
2) 将 PluginInfrastructure 里的代码文件移动过去

3) 在插件项目中添加共享项目引用, 这样一份基础设施代码就能在多个项目之间共享

实现窗口停靠

1) 从官网插件开发向导页面找C#插件示例中参考窗口停靠的具体实现。在GitHub也有在线版
2) 新建一个窗体,命名 SayHiForm,延续上回命名习惯
3) 将示例代码应用到项目中,按 F5 键启动调试验证实现。可以在 SVN 上查看最终实现

点击查看原图

~扯谈时间~

在上一篇文章里我们搞清楚了如何开始用 C# 为 Notepad++ 写插件让我们继续折腾,这次一共有3个折腾点,分别是完善调试体验、用共享项目避免重复代码还有实现窗口停靠。让我们从第一个折腾点开始。

按照TL;DR的步骤操作下来,现在可以命中断点了,也能查看和修改变量值以及拖动执行代码,略遗憾的是编辑并继续不可用。提示未加载程序集时不允许,可能是 DllExport 修改 DLL 导致。总而言之,能打断点已经是很好的体验了。非要在调试时想要编辑并继续也不是不可以,只要另起一个项目作为调试启动入口来调用这个程序集,那么在调试时也是可以编辑并继续的,只不过你得而外地为调试入口做一些适配,从而弥补调试入口和实际被 Notepad++ 加载运行时的差别。扯完这个折腾点之后让我们简单扯扯代码共享项目。

说到代码共享项目那就得先聊聊过去,在没有这种项目类型时,我是如何管理重复出现于多个项目的代码集。在那个时代,我的解决方案有两种,分别是将其独立成为类库,或者用目录符号链接。这两种方法相对于代码共享项目来说都是有明显缺陷的,拿这次我们折腾的,带非托管导出函数的程序集来说,就得把代码放入当前项目中,导出函数不应该出现在引用的程序集;而符号连接虽然能算作当前项目的代码文件,然而命名空间不能与项目名称保持一致,从规范层面来看有些尴尬。用了共享项目之后这两个问题得到了彻底的解决。共享项目能扯的并不多,最后是这次的重头戏--可停靠窗口。

说到可停靠窗口,我要先小声 BB 一下,现在能找到的,能在 Windows 下离线使用的,带实时预览窗格的 Markdown 编辑器中,大部分布局都是文本编辑在左边,预览窗格在右边。只要把预览窗格做成可浮动、停靠的活动面板,这就完美的满足了我这种挑剔家伙和大众普遍习惯的需求。就算是能把预览窗格移动到左边的 vs code,它也不能实现浮动窗口。

回归主题让我们继续吧!进入官方推荐的插件示例项目一看,略震惊。具体实现只需要短短几行代码把窗体句柄按照指定的姿势丢给 NPP,它就能自己管理停靠! TL;DR 里已经写的很清楚,这里就不再啰嗦。

才如果你把整个解决方案Check出来,会发现上一篇文章折腾的 HelloNppPlugin 插件项目并没有使用共享代码项目,这是有意而为之,让内容和文章描述尽可能一致,避免初看的读者受到影响。

这次就扯到这里,下次关于 NPP 插件的文章估计是 Markdown 插件发布的时候了。

标签: 软件开发 插件 C# Interop 调试技术

评论(0) 引用(0) 浏览(256)

在VS15上远程调试Mono应用程序:原来还有这么舒服的姿势!

作者:V君 发布于:2018-10-27 22:48 Saturday 分类:折腾手记

小试一把确认可行之后就来写博文了,稍后再扯,先TLDR:

关键字「mono remote debug」喂狗,吐出「MonoRemoteDebugger」插件。

顺着介绍导航到其GitHub仓库看用法

0)目标机器需要先安装Mono的软件包,这次用的是「mono-devel」
1)下载预编译版本zip到目标机器,解包执行,进入等待状态。
2)在VS里装好插件,新建一个控制台项目,
  点击菜单「
MonoRemoteDebugger -> Debug with Mono (remote)
  在弹出的对话框输入目标机器IP,点击连接按钮。
3)当前应用程序会自动推送到目标机器并以调试模式启动,
  如果在主线程设置断点,将会在断点处停下来,此时可以查看变量值。

需要注意的是Mono调试器并没有原生的那么强大,做不到编辑并继续的体验。
受到远程调试器完善程度的影响,目前还不支持用户线程断点。

不过没关系能打断点看变量值已经很奢侈啦!

进入闲话时间

还记得之前为了调试树莓派GPIO所做的C/S远程控制接口吗?
最近忽然想玩PWM,想试着用它来控制LED灯的亮度。
首先想到的是树莓派自带的Remote gpio功能,然而资料并不多,只能找到py的,没有C#的。
之后换了个想法,如果能直接在上面跑调试那该多好。
虽然知道可以在上面直接安装MonoDevelop来调试,但这货太过于臃肿。
又试着去咕狗看看,结果就找到这货啦!


标签: 软件开发 树莓派 C# 嵌入式 调试技术

评论(0) 引用(0) 浏览(297)

搭建自己的NuGet托管服务器,并通过符号源服务器提供带源码的调试体验

作者:V君 发布于:2018-9-19 19:43 Wednesday 分类:挖坑经验

尽管花了不少时间但都是些坑或者自己没注意,这次不想扯,直接TLDR:


使用命令行把项目打包并推送到服务器 (参考官方文档)

1) 下载最新版NuGet.exe,

2) 为项目编写nuspec文件,可以通过spec参数创建一个模板

3) 分别使用pack命令打包库pack -symbol也生成符号包

4) 使用push命令将这俩分别推送到对应服务器

+) 如果想连同PDB文件一起打包(可以堆栈跟踪里获得源代码位置),可以添加下配置到nuspec根

  <files>
    <file src="bin\$configuration$\$id$.pdb" target="lib\netxxx\" />
  </files>

 这样做有个副作用会让符号包生成时产生警告PDB文件重复

 还有一个副作用会让符号源失效,在DLL旁边PDB似乎挡住了前往符号源服务器的去路,

 导致F11进不去,你得在启动调试之前自己把输出目录的PDB删除

 目前的做法是写成生成事件,仅匹配Debug生成时删除指定的PDB文件


搭建NuGet服务器 (参考自官方文档)

1) 新建空白Web项目,引入NuGet包[NuGet.Server]

2) 修改web.config配置ApiKey或干脆禁用验证

3) 部署到服务器


搭建符号服务器 (参考X.D.的博客文章)

1) 创建空白MVC项目,引入NuGet包[SymbolSource.Server.Basic]

2) 在服务器上安装Windows Kits Debuggers,下载SDK安装器进行可选安装

3) 部署到服务器,配置好web.config指向上者安装路径的srcsrv

4) 如果用了主机头绑定和HOSTS,在服务器也需要配置指向自己的HOSTS

x) 这货好像也能当NuGet包托管服务器?(但貌似不能设置ApiKey验证


在VS使用自己部署的NuGet包服务器

1) 在菜单工具-选项打开选项对话框

2) 在左边选择NuGet包管理器-程序包源,添加服务器地址到列表,确定

3) 在包管理器检索时不要忘记切换到自己的服务器,或简单选择全部


在VS使用调试符号 (参考符号源官网文档)

1) 在菜单工具-选项打开选项对话框

2) 在左边选择调试-符号,添加服务器地址到列表

3) 接着在左边找到兄弟节点常规对里面的选项做一些改动,见参考文档

4) 在调试时对着提供了符号包的NuGet程序集调用按下F11, VS会自动找到源代码并进入调试


扩充话题


在程序集里嵌入Git提交哈希,可以使用[MSbuildGitHash]这个包来轻松完成

但这货有个缺点,如果机器上没安装Git或者当前项目不是Git托管,就会编译失败

需要自己指定MSBuild条件来绕过.


标签: 软件开发 源码管理 调试技术 .NET

评论(0) 引用(0) 浏览(341)

使用Windbg+sos+sosex从内存转储详细分析对象引用,排查.NET服务应用内存暴增

作者:V君 发布于:2018-8-22 12:37 Wednesday 分类:填坑经验

TL;DR

1) 准备:先载入转储

 如果缺少dll, 去 https://sos.debugging.wellisolutions.de/ 找

 下来放windbg旁边,然后重新载入.

2) 初始化:载入sos和sosex [.loadby sos mscorwks],[.load sosex],首次要执行[!bhi]

3) 对象统计:用sos的[!dumpheap -stat]或者sosex的[!sosex.mfrag -stat]获取结果

4) 枚举对象:用sos的[!dumpheap -type <上结果类名>]就能刷出

  sosex的[mfrag -mt:类名]如果能用就可以直接点击链接, 这次不知道为啥不工作.

5) 查找引用:用sosex的[!refs <上结果地址>]

  顺着地址链接一路跟踪就能定位内存占用的代码位置.

 

 

必须扯:

阅读全文>>

标签: 软件开发 C# 调试技术 windbg 软件故障诊断 sosex

评论(0) 引用(0) 浏览(406)

活久见:表达式树编译执行引发VerificationException—操作可能会破坏运行时稳定性

作者:V君 发布于:2018-2-7 0:04 Wednesday 分类:挖坑经验

TL;DR:

当在使用表达式树编译成员绑定表达式,且存在隐式值类型装箱,

将表达式树编译成的委托执行起来的时候将会引发异常VerificationException

并提示消息“操作可能会破坏运行时稳定性

解决方法为显式装箱之后再绑定,可能是表达式树在编译成IL时没有对其进行必要的校验。

至于是不是性能考虑就不晓得了。从结果上来看,似乎却在运行时(亦或者JIT时)有检查。


听我扯扯:

在项目中需要比较两个同类型对象实例,且将差异收集起来。

于是咱首先就想到:不要一遍遍写繁琐的判断!

而是使用动态行为——堆IL、拼代码动态编译或表达式树编译。

咱选择后者,比起堆IL和拼代码还是表达式树写起来更舒服。


就算使用动态行为,基础的动作还是由简单的逻辑语句组成。

首先要遍历要diff的类的全部属性,然后将两个实例对应的属性值读出来比较,

若值相等则忽略,若值差异则连同属性名和两个差异的值收集起来。


思路想清楚了就开始吧。


定义一个有3个属性的类,分别存放属性名,和两个值,作为差异条目模型。

命名DiffEntry属性名是字符串,另外两个是object。


接着定义一个静态泛型类,静态构造里面构建每个属性的比较表达式树并将它们编译成委托,

公开静态方法Diff,返回DiffEntry的数组,调用编译好的委托。

命名ObjDiffCollector,存在引发装箱异常问题。

在gist描述为修订2,因为最初使用数组存放编译出来的委托,发生异常之后,

为了调查异常引发原因缩小范围,改为字典,键是属性名,值是委托,仅用在调试查看。


将问题缩小到值类型在DiffEntity的构造初始化成员绑定之后,

          .↑字符串类型没有问题

开始一小会儿的懵逼,放狗出去并没有找到什么卵线索。


这段可以忽略

先贴上异常名称,咕狗很贴心地提示了“operation could destabilize the runtime”,

接着出来的只有一个爆栈帖子,提到使用一个第三方数据访问层引发这样的错误。

打住吧,换成中文消息搜一下看看。

和预料的一样然并卵,被一篇转载到成为互联网垃圾程度的文章刷屏。

文章清一色的提到使用redgate的性能测量库所引发。

到这里线索断了。时间也不早,还要赴约,就把问题撂下跑了。

离开电脑面前不久之后,又玩了一次当局者迷play。

在外头晃悠时忽然灵光一现想到可能是隐式装箱导致,最终证实这个想法。


THE MEAT AND POTATOES 圈重点啦

可能堆IL和表达式编译一样,没有经过太完整的代码转换过程,隐式行为需要显式表达。

这时候需要将绑定的表达式加一层,转换成object,也就是显式装箱。

最终得出可用的ObjDiffCollector


回过头来想一想,发现CLR报这个错误也不无道理。

试想一下:一个非引用类型的结构体被装箱的情形,如果再复杂一点如果和原生代码扯上关系。

可能会导致意外的值复制行为,从而影响到整个应用程序的稳定性。

想当年,玩DllImport时,一不小心就内存损坏或者堆栈不平衡……


到此为止啦,收工!


接下来该考虑如何一步到位了,或许可以在一个表达式里面作完所有属性的比较和收集差异。

让代码的逼格显得更高。 乂目

标签: 软件开发 C# 调试技术 JIT LINQ 运行时错误

评论(0) 引用(0) 浏览(380)

Powered by emlog 去你妹的备案 sitemap