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条)

报告相同问题?

悬赏问题

  • ¥20 关于php录入完成后,批量更新数据库
  • ¥15 请教往复密封润滑问题
  • ¥15 cocos creator发布ios包
  • ¥15 comsol压电材料数据
  • ¥35 用python实现除法算法中goldschmidt算法
  • ¥15 汇编代码转换成C代码
  • ¥15 除法算法中的归一化具体是怎么变的?
  • ¥20 集成电路的逻辑电路和晶体管简化图
  • ¥15 下载windows builder后的问题
  • ¥15 端口连接数为什么会有限制