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

第三方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 Stata链式中介效应代码修改
  • ¥15 latex投稿显示click download
  • ¥15 请问读取环境变量文件失败是什么原因?
  • ¥15 在若依框架下实现人脸识别
  • ¥15 添加组件无法加载页面,某块加载卡住
  • ¥15 网络科学导论,网络控制
  • ¥15 利用Sentinel-2和Landsat8做一个水库的长时序NDVI的对比,为什么Snetinel-2计算的结果最小值特别小,而Lansat8就很平均
  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错