我的任务是让一些 C# 代码在 x64 中工作,这些代码调用名为 Detagger 的 native x64 dll,用于将 HTML 转换为文本,同时保持 HTML 的基本结构。
此代码在为 C# 代码和 dll 的 x86 构建平台目标 x86 运行时运行了多年,但在将平台目标设置为 x64 并使用 dll 的 x64 构建时它崩溃了。事实上,如果 C# 应用程序是使用 .Net Framework 3.5 或更低版本构建的,则 x64 可以正常工作。使用 4.0 或更高版本构建时会崩溃。
有问题的 dll 具有以下 header :
#ifdef WIN32
#ifdef USE_DLL
#ifdef DLL_EXPORTS
#define DLL_DECLARE __declspec(dllexport) long __stdcall
#else
#define DLL_DECLARE __declspec(dllimport) long __stdcall
#endif
#else
#define DLL_DECLARE long
#endif
#else
#define DLL_DECLARE long
#endif
...
DLL_DECLARE CONVERTER_Allocate (); // returns non-zero Handle if succeeds
...
DLL_DECLARE CONVERTER_ResetPolicies (long Handle);
因此 API 需要调用 CONVERT_Allocate() 函数来获取“句柄”(我认为这实际上是一个内存地址),然后将该“句柄”传递给所有其他方法。我认为这是为了使调用线程安全。
我现在尝试着重介绍 CONVERTER_ResetPolicies() 函数,因为它是最基本的函数之一,只需要一个参数(“句柄”)。整个API中的函数没有一个是复杂的,都采用基本类型或指针等参数(无结构)。
从 C++ 头文件来看,调用约定应该是 stdcall,dll 中的每个导出函数都返回一个 long(在 x86 和 x64 中都应该是 4 个字节)。我对 x64 的理解是它的调用约定基本上总是 fastcall 的变体,所以我对 stdcall 很好奇,但它适用于 .Net 3.5 及以下版本,所以这是改天的问题。
供应商为dll提供的PInvoke签名是:
// DLL_DECLARE CONVERTER_Allocate();
[DllImport(_dll, EntryPoint = "CONVERTER_Allocate")]
public static extern IntPtr Allocate();
// DLL_DECLARE CONVERTER_ResetPolicies(long Handle);
[DllImport(_dll, EntryPoint = "CONVERTER_ResetPolicies")]
public static extern APIResult ResetPolicies(IntPtr handle);
给定以下 C# 代码:
IntPtr handle = DetaggerAPI.Allocate();
var result = DetaggerAPI.ResetPolicies();
这会在调用 CONVERTER_ResetPolicies() 时崩溃。进入调试器会显示以下内容:
在 C# 中: 句柄 = 0x00000000e82d0080
在进入 DLL 后的反汇编中:
寄存器和标志:
RAX = 000000018001B490 RBX = 0000000FCC66EB68 RCX = 00000000E82D0080
RDX = 0000000FCC66EC80 RSI = 0000000FCF8B44A8 RDI = 0000000FCC66E980
R8 = 00001EB6102A86D4 R9 = 0000000FE84C4001 R10 = 00007FF9497961F0
R11 = 0000000000000000 R12 = 0000000000000000 R13 = 0000000FCC66EAF0
R14 = 0000000FCC66EB68 R15 = 0000000000000004 RIP = 000000018001B490
RSP = 0000000FCC66E848 RBP = 0000000FCC66E850 EFL = 00000246
CS = 0033 DS = 0000 ES = 0000 SS = 002B FS = 0000 GS = 0000
OV = 0 UP = 0 EI = 1 PL = 0 ZR = 1 AC = 0 PE = 1 CY = 0
请注意,handle 的值在 RCX (e82d0080) 中。
这是反汇编(我添加的一些评论):
000000018001B490 sub rsp,28h ; subtract 40 from stack pointer, sets up stack frame
000000018001B494 call 000000018001B090
000000018001B090 push rbx
000000018001B092 sub rsp,20h ; subtract 32 from stack pointer, sets up stack frame
000000018001B096 test ecx,ecx ; check if ecx is 0
000000018001B098 movsxd rbx,ecx ; move value in ecx (the handle passed in) to rbx and sign-extend it to qword
; rbx changes from 0000000FCC66EB68 to FFFFFFFFE82D0080
000000018001B09B je 000000018001B0C6 ; if ecx is 0, probably jump to a function that returns an error
-> 000000018001B09D cmp dword ptr [rbx],4D2h ; compare value pointed to by rbx (as a dword) to 042d (1234),
; but rbx points to FFFFFFFFE82D0080, which is probably an invalid memory location,
; so !!this is the line that crashes !!
000000018001B0A3 jne 000000018001B0C6 ; jump if not equal
000000018001B0A5 mov ecx,dword ptr [1801122C0h]
000000018001B0AB mov dword ptr [rbx+2F0B0h],ecx
000000018001B0B1 lea rcx,[rbx+2F0B8h]
000000018001B0B8 call 00000001800A7C40
000000018001B0BD mov rax,rbx
000000018001B0C0 add rsp,20h
000000018001B0C4 pop rbx
000000018001B0C5 ret
000000018001B499 test rax,rax
000000018001B49C jne 000000018001B4BC
000000018001B49E cmp dword ptr [1801122C0h],eax
000000018001B4A4 je 000000018001B4B2
000000018001B4A6 lea rcx,[1800D7B70h]
000000018001B4AD call 000000018001B290
000000018001B4B2 mov eax,2 ; if we got here, return 2 in eax, meaning APIResult.Invalid. Note that this is 32bits.
000000018001B4B7 add rsp,28h ; clean up stack frame
000000018001B4BB ret ; return
所以,看起来“句柄”正在 RCX 中传递,然后是
movsxd rbx,ecx
指令是将这个句柄复制到 RBX 中,但也基本上破坏了它,因为它看起来是一个内存地址,而不仅仅是一些不透明的句柄,它是一个数组索引或类似的东西。然后两条指令之后我从指令中得到访问冲突
cmp dword ptr [rbx],4D2h
因为这是试图取消引用指向垃圾的 RBX。
根据 https://msdn.microsoft.com/en-us/library/ee941656(v=vs.100).aspx#core ,在 Platform Invoke 下,它说 3.5 SP1 和 4.0 之间的区别是:
To improve performance in interoperability with unmanaged code, incorrect calling conventions in a platform invoke now cause the application to fail. In previous versions, the marshaling layer resolved these errors up the stack.
这有点含糊,但由于我在这里唯一的选择是 stdcall(不支持 fastcall),我认为这是正确的,而不是问题所在。
我要尝试的一些事情:
如果有人能发现这里发生的事情或给我任何想法,那就太好了。
最佳答案
正如您所提到的,程序集显然是将句柄作为指针进行访问。这意味着它应该是一个指针,但由于 long在 Windows 上始终是 32 位,它不起作用。
这可能是一个错误,C++ 代码不应该使用 long .这可能是为 linux 编写的代码,因为 long在 Linux 上是 64 位的(依赖编译器定义的大小仍然是错误的)。
我建议您将所有出现的句柄的类型替换为intptr_t (在 <cstdint>/<stdint.h> 中为 linux 和 Windows 定义),以获得 [可能的] 预期行为。实际上,替换所有 long 可能是个好主意。通过 intptr_t ,因为错误可能无处不在。
编辑:由于代码最初使用普通整数类型,intptr_t可能更安全,但理想的解决方案是对 void* 使用 typedef ,这将在任何地方都有效并且更有意义。如果您看到使用 void*没有发现任何问题,请改用它(仅用于句柄)。
关于c# - PInvoke x64 崩溃与 .Net 4.0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32460514/
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.