doutuichan2681 2018-09-10 07:30
浏览 114
已采纳

第三方dll中未处理的delphi异常

I have a dynamic library from which I need to call a procedure in my Go program, but I can't seem to get it right. I have another program written in C# which uses this DLL and according to dnSpy dllimport compiles to:

[DllImport("Library.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
    public static extern void Calc(double[] Input, [MarshalAs(UnmanagedType.VBByRefStr)] ref string Pattern, [MarshalAs(UnmanagedType.VBByRefStr)] ref string Database_path, double[] Output);

So basing on this I tried to call it in my code like so:

func main() {
    lib := syscall.NewLazyDLL("Library.dll")
    proc := lib.NewProc("Calc")

    input := [28]float64{0.741, 0.585, 2, 12, 1, 1, 1, 101325, 2500, 3, 20, 17.73, 17.11, 45, 1, 0, 80, 60, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0}
    output := [20]float64{}
    tubePattern := marshalAnsi("P8-16G-E145/028")
    basePath, _ := filepath.Abs(".")
    databasePath := marshalAnsi(basePath)
    a1 := uintptr(unsafe.Pointer(&input))
    a2 := uintptr(unsafe.Pointer(tubePattern))
    a3 := uintptr(unsafe.Pointer(databasePath))
    a4 := uintptr(unsafe.Pointer(&output))
    ret, _, _ := proc.Call(a1, a2, a3, a4)
    log.Println(ret)
    log.Println(output)
}

func marshalAnsi(input string) *[]byte {
    var b bytes.Buffer
    writer := transform.NewWriter(&b, charmap.Windows1252.NewEncoder())
    writer.Write([]byte(input))
    writer.Close()
    output := b.Bytes()
    return &output
}

This results in unhandled delphi exception:

Exception 0xeedfade 0x31392774 0x32db04e4 0x7677ddc2
PC=0x7677ddc2

syscall.Syscall6(0x314af7d0, 0x4, 0x10ee7eb8, 0x10ed21a0, 0x10ed21d0, 0x10ee7d78, 0x0, 0x0, 0x0, 0x0, ...)
        C:/Go32/src/runtime/syscall_windows.go:184 +0xcf
syscall.(*Proc).Call(0x10ed21e0, 0x10ed85e0, 0x4, 0x4, 0x10, 0x49bee0, 0x533801, 0x10ed85e0)
        C:/Go32/src/syscall/dll_windows.go:152 +0x32e
syscall.(*LazyProc).Call(0x10ecb9c0, 0x10ed85e0, 0x4, 0x4, 0x0, 0x0, 0x4c6d70, 0x5491a0)
        C:/Go32/src/syscall/dll_windows.go:302 +0x48
main.main()
        C:/Users/Krzysztof Kowalczyk/go/src/danpoltherm.pl/dllloader/main.go:32 +0x2c0
eax     0x19f3f0
ebx     0x31392774
ecx     0x7
edx     0x0
edi     0x10ee7d78
esi     0x31392774
ebp     0x19f448
esp     0x19f3f0
eip     0x7677ddc2
eflags  0x216
cs      0x23
fs      0x53
gs      0x2b

I believe that problem might lie in the way I pass these strings to procedure, but I can't see what my code does different than that C# application.

Library have a few other procedures, I can call DLL_Version with no problems like so:

lib := syscall.NewLazyDLL("Library.dll")
verProc := lib.NewProc("DLL_Version")
var version float64
verProc.Call(uintptr(unsafe.Pointer(&version)))
log.Printf("DLL version: %f
", version)

Outputs:

2018/09/10 09:13:11 DLL version: 1.250000

EDIT: I believe that I found out what causes the exception. It seems that when called from my code, 2nd and 3rd parameters for this procedure, which should be pointers to string buffers, are instead pointers to pointers on these buffers, if I break on the beginning of procedure and patch this manually, code runs and terminates correctly. I still did not found a reason why this happens, and how to fix it though.

EDIT2: Apparently my previous assessment of this case was invalid. I was able to stop program from throwing exception, but I analyzed assembly and it turned out it was only due to program exiting before this exception was thrown, and instead of exception I got normal error code about invalid string.

While I'm not really good at understanding disassembled code, it seems that problem might indeed be caused by calling convention mismatch. At the beginning of procedure code, when called from working program EAX, EBX, ECX and EDX registers are empty, meanwhile when called from my code, only EAX and ECX are empty, EDX holds something but EBX points to Calc procedure pointer in my executable. Later in the DLL there is a part which checks if EBX is empty and throws exception if not.

EDIT 3: Well, that seems to also be unrelated, I'm aware that there is too little information for anyone to be able to make any guess as to what is going on. So I'll try to track where this exception comes from exactly and come back to this question.

  • 写回答

2条回答 默认 最新

  • dqrdlqpo775594 2018-09-24 13:52
    关注

    Okay, so I've finally found an answer. As @kostix noticed go-ole was required, since DLL uses COM and I had to call CoInitializeEx first.

    But that was only part of this, turns out string parameters of procedure were declared as UnicodeString, which required me to put together a conversion function to properly fit into layout described in documentation: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Unicode_in_RAD_Studio#New_String_Type:_UnicodeString

    And once that was done, all started to work as intended.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 file converter 转换格式失败 报错 Error marking filters as finished,如何解决?
  • ¥15 ubuntu系统下挂载磁盘上执行./提示权限不够
  • ¥15 Arcgis相交分析无法绘制一个或多个图形
  • ¥15 关于#r语言#的问题:差异分析前数据准备,报错Error in data[, sampleName1] : subscript out of bounds请问怎么解决呀以下是全部代码:
  • ¥15 seatunnel-web使用SQL组件时候后台报错,无法找到表格
  • ¥15 fpga自动售货机数码管(相关搜索:数字时钟)
  • ¥15 用前端向数据库插入数据,通过debug发现数据能走到后端,但是放行之后就会提示错误
  • ¥30 3天&7天&&15天&销量如何统计同一行
  • ¥30 帮我写一段可以读取LD2450数据并计算距离的Arduino代码
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型