无法处理在Windows上返回char *的自定义dll(ImageSearch.dll)

时间:2018-07-09 16:19:27

标签: go

信息: 在写完全文之后,我遇到了some other SO post,又尝试了一次,它才起作用。但是,由于对我来说解决方案(甚至是问题)一点都不明显(并且编写此问题的工作量很大),因此我决定仍然将此文本与解决方案一起发布。希望这对some developer coming across this post in the future有帮助。

由于我对SO还很陌生,所以我真的不知道这是否值得赞赏,所以随时让我知道将其删除。

TLDR:

我正在尝试调用DLL,该DLL需要一个字符串参数并返回一个字符串。我尝试了几种无济于事的解决方案和方法。最后-偶然-我发现问题出在传递字符串的方式上。

我的解决方法是:

imagePath := "*30 C:\\absolute\\path\\to\\test.png"
bytePath := []byte(imagePath + "\x00") // Make string null-terminated (required by C)

dll := syscall.NewLazyDLL("ImageSearchDLL.dll")
imageSearch := dll.NewProc("ImageSearch")

r1, _, _ := imageSearch.Call(
    0,
    0,
    1920,
    1080,
    uintptr(unsafe.Pointer(&bytePath[0]))) // Make sure to pass index 0

// DLL returned char*, parse that to a GoString
var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(r1))
hdr.Len = 32 // Adjust length as needed!

fmt.Println(s)

char*C.CString可能以某种方式将C.GoString解析为字符串并反向,我无法在足够短的时间内完成该工作,所以我决定保持这种方式。

还应该检查字符串的长度(由\ x00终止)。


原始问题

我还很陌生,但是想在下一个桌面自动化项目中使用它。为此,我正在尝试查找显示器中存在的图像。 由于找不到任何能够执行此操作的go库,因此我决定尝试使用几年前Autoit经验中已知的DLL:DLL Download Source Code

但是,经过数小时的尝试找到解决方案(包括the wiki)之后,我无法正常工作。

库/函数本身具有以下签名: char* WINAPI ImageSearch(int aLeft, int aTop, int aRight, int aBottom, char *aImageFile)

由于我对C一点都不熟悉,因此我尝试了提出的其他两种解决方案:

path := "*30 C:\\absolute\\path\\to\\test.png"
dll := syscall.NewLazyDLL("ImageSearchDLL.dll")
imageSearch := dll.NewProc("ImageSearch")
r1, r2, r3 := imageSearch.Call(0, 0, 1920, 1080, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(imagePath))))
fmt.Println(r1, r2, r3)

和:

path := "*30 C:\\absolute\\path\\to\\test.png"
dll2, _ := syscall.LoadLibrary("ImageSearchDLL.dll")
imageSearch2, _ := syscall.GetProcAddress(dll2, "ImageSearch")

r1, r2, r3 = syscall.Syscall6(imageSearch2,
    5,
    uintptr(0),
    uintptr(0),
    uintptr(1920),
    uintptr(1920),
    uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(imagePath))),
    0)

fmt.Println(r1, r2, r3)

请注意,路径变量是根据this solution using C#设置的。

两者的输出如下:

6442522512 0 Der Vorgang wurde erfolgreich beendet.

含义:“该过程已成功完成”。

由于DLL应该返回一个char*,所以我打开了documentation of unsafe并找到了Pointer>(6)。使用猜测的字符串长度,我想出了这个尝试:

imagePath := "*30 C:\\absolute\\path\\to\\test.png"
dll := syscall.NewLazyDLL("ImageSearchDLL.dll")
imageSearch := dll.NewProc("ImageSearch")
r1, r2, r3 := imageSearch.Call(0, 0, 1920, 1080, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(imagePath))))
fmt.Println(r1, r2, r3)

var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(r1))
hdr.Len = 64

fmt.Println(s)

这至少可以打印出某种字符串:

6442522512 0 Der Vorgang wurde erfolgreich beendet.
0   0   0   0   0       1|%d|%d|%d|%d   0       c:\pic.bmp      

据我了解,该问题可能是由3种可能的问题引起的:

  1. 我在使用ImageSearch时出错
  2. 将字符串传递给自定义dll在Go中无法正常工作
  3. “返回的” char*的解析不起作用

由于我必须了解如何处理2和3,因此我至少想确保不是1。因此,我在Autoit中进行了快速测试,返回了预期的结果:

#include "array.au3"
$res = DllCall("ImageSearchDLL.dll", "str", "ImageSearch", "int", 0, "int", 0, "int", 1920, "int", 1080, "str", "*30 C:\\absolute\\path\\to\\test.png")

_ArrayDisplay($res)

输出:

Row|Col 0
[0]|1|505|1051|21|20
[1]|0
[2]|0
[3]|1920
[4]|1080
[5]|*30 C:\\absolute\\path\\to\\test.png

这使我充满信心,至少可以正确使用dll。但是,这也导致我真的对其他尝试一无所知。看看cgo时,我并没有真正了解要如何实现自己想要实现的目标,尤其是在没有C知识的情况下。

您对我的进一步方法有什么建议吗?还有什么可以尝试的?

0 个答案:

没有答案