[求助]Julia在Windows下调用C语言报错


#1

我是一个使用Julia的新手,尝试在windows 10 下使用Julia调用使用VS 2017编写的dll文件。可是我尝试了网络上众多的方法,目前还只一个成功

# code
julia> t = ccall((:clock, "msvcrt"), Int32, ())
6592

这是我VS2017 中使用Windows桌面向导创建工程时,选择“动态链接库”与“导出符号”、“预编译标头”、“安全开发声明周期(SDL)检查”时,VS创建的三个头文件与三个源文件,我尝试使用Julia调用fnlearn函数,但都失败了。

# code
/**************************learndll.h*******************************/
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 LEARNDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// LEARNDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef LEARNDLL_EXPORTS
#define LEARNDLL_API __declspec(dllexport)
#else
#define LEARNDLL_API __declspec(dllimport)
#endif

// 此类是从 dll 导出的
class LEARNDLL_API Clearndll {
public:
	Clearndll(void);
	// TODO: 在此处添加方法。
};

extern LEARNDLL_API int nlearndll;

LEARNDLL_API int fnlearndll(void);

/***************************************************************/
/**************************stdafx.h****************************/
// stdafx.h: 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 项目特定的包含文件
//

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN             // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>



// 在此处引用程序需要的其他标头
/*********************************************************************/
/****************************targetver.h*****************************/
#pragma once

// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。

// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并
// 将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。

#include <SDKDDKVer.h>
/*********************************************************************/
/**************************dllmain.cpp******************************/
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
/*********************************************************************/
/*****************************learndll.cpp***************************/
// learndll.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"
#include "learndll.h"


// 这是导出变量的一个示例
LEARNDLL_API int nlearndll=0;

// 这是导出函数的一个示例。
LEARNDLL_API int fnlearndll(void)
{
    return 42;
}

// 这是已导出类的构造函数。
Clearndll::Clearndll()
{
    return;
}
/*********************************************************************/
/****************************stdafx.cpp*****************************/
#include "stdafx.h"
/*********************************************************************/

但是不论使用Release的32位还是64位编译:

# code
julia> ccall((:fnlearndll,"C:\\Users\\WeiM\\source\\repos\\learndll\\Release\\LEARNDLL.dll"),Cint,())#Release x86编译
ERROR: error compiling top-level scope: could not load library "C:\Users\WeiM\source\repos\learndll\Release\LEARNDLL.dll"
%1 is not a valid Win32 application.

julia> ccall((:fnlearndll,"C:\\Users\\WeiM\\source\\repos\\learndll\\x64\\Release\\LEARNDLL.dll"),Cint,())#Release x64编译
ERROR: ccall: could not find function fnlearndll in library C:\Users\WeiM\source\repos\learndll\x64\Release\LEARNDLL.dll
Stacktrace:
 [1] top-level scope at .\none:0

另外这是我的版本信息,Windows 10,64位 1809

# code
julia> versioninfo()
Julia Version 1.1.0
Commit 80516ca202 (2019-01-21 21:24 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Genuine Intel(R) CPU 0000 @ 2.90GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, skylake)

我研究这个问题已经快一周了,还是不能使用Julia调用自己编写的C语言函数。感觉Julia与C都快有生殖隔离了……


#2

你的dll是64位的吗?


#3

32位,64位我都尝试过


#4

C和C++不是一种语言。

这里已经给了很明显的提示了,找不到函数名,因为C++的mangling与C不一样,需要用extern “C” 来确保mangling一致。


#5

谢谢您,我在learndll.h头文件添加extern "C"后就成功调用了!:star_struck:

# code
julia> ccall((:fnlearndll,"C:\\Users\\WeiM\\source\\repos\\learndll\\x64\\Release\\LEARNDLL.dll"),Cint,())
42

希望后面的文档能给一个自己编写C函数(helloword什么的),再用Julia调用的例子,对于像我这样的新手应该很有帮助
我对C++和库调用编写了解不多,还有很多要学习啊:grin:
下面给出我修改后的learndll.h,也许能帮助像我一样的人吧

# code
/*************************************learndll.h*************************************/
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 LEARNDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// LEARNDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef LEARNDLL_EXPORTS
#define LEARNDLL_API __declspec(dllexport)
#else
#define LEARNDLL_API __declspec(dllimport)
#endif

// 此类是从 dll 导出的
class LEARNDLL_API Clearndll {
public:
	Clearndll(void);
	// TODO: 在此处添加方法。
};

extern LEARNDLL_API int nlearndll;
extern "C"
{
	LEARNDLL_API int fnlearndll(void);
}
/****************************************end****************************************/