自由DLL接口规范
(→调用.Net DLL) |
(→返回值类型) |
||
(未显示2个用户的12个中间版本) | |||
第1行: | 第1行: | ||
=简介= | =简介= | ||
− | 相比传统DLL接口( | + | 相比传统DLL接口(飞狐接口),自由DLL接口的特点是参数更加灵活,由用户指定传入的参数,参数类型是c/c++的标准数据类型。 |
− | + | 传统DLL必须引入一个CalcInfo的结构化参数,通过它来传入行情数据,这种设计的好处是用户操心得少一些,因为数据总是有的,不利之处是限制了发挥,难以返回多个序列结果,难以接入没有按接口要求编写的DLL。 | |
− | + | 自由DLL接口,不再传入CalcInfo参数,取而代之的是,用户必须自己负责传入所有的数据。获得的回报是, | |
− | |||
− | |||
− | |||
− | |||
1.支持任意多的参数。 | 1.支持任意多的参数。 | ||
+ | |||
2.支持引用参数,也就是说,可以改变参数的值并返回给公式系统。 | 2.支持引用参数,也就是说,可以改变参数的值并返回给公式系统。 | ||
+ | |||
3.更灵活的参数传递。例如,可以根据需要传入地址,也可以传入值。 | 3.更灵活的参数传递。例如,可以根据需要传入地址,也可以传入值。 | ||
− | |||
+ | 4.支持逐根的计算。每次只计算一根的值并返回单值。自由DLL只能在逐根模式下运行。 | ||
与传统接口DLL一样,自由接口DLL也必须放置在FmlDLL目录下。 | 与传统接口DLL一样,自由接口DLL也必须放置在FmlDLL目录下。 | ||
第22行: | 第20行: | ||
为了让公式系统知道DLL的参数格式,调用前需要使用extern语句进行声明 | 为了让公式系统知道DLL的参数格式,调用前需要使用extern语句进行声明 | ||
− | extern 返回值类型 函数名( | + | extern 返回值类型 函数名(参数类型1, 参数类型2...); |
− | + | 参数的个数没有限制 | |
=参数类型= | =参数类型= | ||
第30行: | 第28行: | ||
传入DLL的参数有类型限制,目前支持的C类型包括 | 传入DLL的参数有类型限制,目前支持的C类型包括 | ||
float, double, int,BOOL, LONG, DWORD, float*, LPCWSTR等 | float, double, int,BOOL, LONG, DWORD, float*, LPCWSTR等 | ||
− | + | 不支持double * | |
− | + | =返回值类型= | |
− | + | ||
− | + | ||
− | + | 函数可以返回 int, float, char, bool 等类型,或无返回。 | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | 若要在DLL内修改一条序列或者修改整个数组,可以通过传序列参数或数组参数的方法,在DLL里面赋值。DLL 里面都使用 float * 来接收参数。 | |
− | + | =自由DLL接口的调用= | |
− | + | *声明过的函数,可以通过函数名直接调用,无需通过"DLLName@FuncName"格式或RefDLL使用。 | |
− | + | *使用时必须将DLL放在软件安装目录下的FmlDll子目录下 | |
+ | *字符串使用单引号,双引号是另一种格式的引用,不能混用 | ||
− | + | *DLL 需要在 .def 文件中 EXPORTS 项添加要调用的函数,否则有可能找不到函数 | |
− | + | ||
− | + | *DLL名称和函数名都是大小写敏感的,代码声明和公式调用时需要匹配。 | |
− | + | =DLL公式参数传递例子= | |
− | + | *无参数时的例子: | |
+ | // 公式里面的声明与调用 | ||
+ | extern 'FoxFunc.dll' void test1(); | ||
+ | test1(); | ||
− | + | // c++ .h 函数声明 | |
+ | __declspec(dllexport) void __stdcall test1(); | ||
+ | |||
+ | // c++ .cpp 函数实现 | ||
+ | extern "C" __declspec(dllexport) void __stdcall test1() | ||
+ | { | ||
+ | |||
+ | } | ||
+ | |||
+ | *传入单值的公式例子: | ||
+ | |||
+ | // 公式里面的声明与调用 | ||
+ | extern 'FoxFunc.dll' void test2(int, float, bool, char); | ||
+ | Variables: | ||
+ | NumericSimple var1, | ||
+ | NumericSeries var2, | ||
+ | NumericArray var3[10]; | ||
+ | |||
+ | var1 := 1; | ||
+ | var2[0] := 2; | ||
+ | var3[0] := 65; | ||
+ | |||
+ | test2(1, 2, 1, 65); | ||
+ | test2(var1, var2[0], var1, var3[0]); | ||
+ | |||
+ | // c++ .h 函数声明 | ||
+ | __declspec(dllexport) void __stdcall test2(int i, float f, bool b, char c); | ||
+ | |||
+ | // c++ .cpp 函数实现 | ||
+ | extern "C" __declspec(dllexport) void __stdcall test2(int i, float f, bool b, char c) | ||
+ | { | ||
+ | |||
+ | } | ||
+ | |||
+ | *传入字符串的公式例子 | ||
+ | |||
+ | 公式里面的字符串变量是宽字符,编码是GB2312,DLL里面可以使用MultiByteToWideChar/MultiByteToWideChar进行字符转换 | ||
+ | |||
+ | // 公式里面的声明与调用 | ||
+ | extern 'FoxFunc.dll' void test3(LPCWSTR); | ||
+ | Variables: | ||
+ | String var1; | ||
+ | |||
+ | var1 := 'test string1'; | ||
+ | |||
+ | test3(NumToStr(close,3)); | ||
+ | test3('aaa'); | ||
+ | test3(var1); | ||
+ | |||
+ | // c++ .h 函数声明 | ||
+ | __declspec(dllexport) void __stdcall test3(const wchar_t* p); | ||
+ | |||
+ | // c++ .cpp 函数实现 | ||
+ | #include <string> | ||
+ | |||
+ | // 窄字符转宽字符 | ||
+ | static std::wstring a2w(LPCSTR lpa) | ||
+ | { | ||
+ | size_t l = MultiByteToWideChar(936, 0, lpa, -1, NULL, 0); | ||
+ | |||
+ | std::wstring s; | ||
+ | s.resize(l - 1); | ||
+ | |||
+ | MultiByteToWideChar(936, 0, lpa, (int)strlen(lpa), (LPWSTR)s.c_str(), (int)s.size()); | ||
+ | |||
+ | return s; | ||
+ | } | ||
+ | |||
+ | // 宽字符转窄字符 | ||
+ | static std::string w2a(LPCWSTR lpw) | ||
+ | { | ||
+ | size_t l = WideCharToMultiByte(936, 0, lpw, -1, NULL, 0, NULL, NULL); | ||
+ | |||
+ | std::string s; | ||
+ | s.resize(l - 1); | ||
+ | |||
+ | WideCharToMultiByte(936, 0, lpw, (int)wcslen(lpw), (LPSTR)s.c_str(), (int)s.size(), NULL, NULL); | ||
+ | |||
+ | return s; | ||
+ | } | ||
+ | |||
+ | extern "C" __declspec(dllexport) void __stdcall test3(const wchar_t *p) | ||
+ | { | ||
+ | std::wstring sw = p; | ||
+ | std::string sa = w2a(sw.c_str()); | ||
+ | } | ||
+ | |||
+ | *传入序列的公式例子 | ||
+ | |||
+ | // 公式声明与调用 | ||
+ | extern 'FoxFunc.dll' void test4(NumericSeries, int, NumericArray, int); | ||
+ | Variables: | ||
+ | NumericSeries var1, | ||
+ | NumericArray var2[5]; | ||
+ | |||
+ | var1 := close; | ||
+ | var2[1] := 1; | ||
+ | var2[2] := 2; | ||
+ | var2[3] := 3; | ||
+ | var2[4] := 4; | ||
+ | var2[5] := 5; | ||
+ | |||
+ | //var2 数组实际大小是5+1 | ||
+ | test4(var1, BarsCount(close), var2, 6); | ||
+ | |||
+ | // c++ .h 声明 | ||
+ | __declspec(dllexport) void __stdcall test4(float *pf1, int n1, float *pf2, int n2); | ||
+ | |||
+ | // c++ .cpp 函数实现 | ||
+ | extern "C" __declspec(dllexport) void __stdcall test4(float *pf1, int n1, float *pf2, int n2) | ||
+ | { | ||
+ | |||
+ | } | ||
+ | |||
+ | *传入引用的公式例子 | ||
+ | |||
+ | // 公式声明与调用 | ||
+ | extern 'FoxFunc.dll' void test5(NumericRef); | ||
+ | Variables: | ||
+ | NumericSimple var1; | ||
+ | |||
+ | var1 := 3; | ||
+ | test5(var1); | ||
+ | comment(var1); | ||
+ | |||
+ | // c++ .h 声明 | ||
+ | __declspec(dllexport) void __stdcall test5(float *pf); | ||
+ | |||
+ | // c++ .cpp 实现 | ||
+ | extern "C" __declspec(dllexport) void __stdcall test5(float *pf) | ||
+ | { | ||
+ | *pf = 9; | ||
+ | } | ||
=DLL的返回值= | =DLL的返回值= | ||
+ | |||
新格式DLL可返回单值,序列和数组。单值类型大致分为两种:数值类型和字符串类型,可以通过函数返回值直接返回。 | 新格式DLL可返回单值,序列和数组。单值类型大致分为两种:数值类型和字符串类型,可以通过函数返回值直接返回。 | ||
+ | |||
*返回单值类型的c语言例子 | *返回单值类型的c语言例子 | ||
− | // | + | |
− | extern "C" __declspec(dllexport) int | + | // 公式声明与调用例子 |
+ | extern 'FoxFunc.dll' int test6(); | ||
+ | extern 'FoxFunc.dll' float test7(); | ||
+ | extern 'FoxFunc.dll' LPCWSTR test8(); | ||
+ | |||
+ | k1: test6(),OwnerScale; | ||
+ | k2: test7(),OwnerScale; | ||
+ | comment(test8); | ||
+ | |||
+ | // c++ .h 声明 | ||
+ | __declspec(dllexport) int __stdcall test6(); | ||
+ | __declspec(dllexport) float __stdcall test7(); | ||
+ | __declspec(dllexport) wchar_t* __stdcall test8(); | ||
+ | |||
+ | // c++ .cpp 实现 | ||
+ | extern "C" __declspec(dllexport) int __stdcall test6() | ||
{ | { | ||
return 1; | return 1; | ||
} | } | ||
− | + | extern "C" __declspec(dllexport) float __stdcall test7() | |
− | extern "C" __declspec(dllexport) float | + | |
{ | { | ||
− | return | + | return 0.5f; |
} | } | ||
− | + | extern "C" __declspec(dllexport) wchar_t* __stdcall test8() | |
− | extern "C" __declspec(dllexport) | + | |
{ | { | ||
− | return L" | + | return L"test string 1"; |
} | } | ||
− | + | ||
− | + | *返回序列或数组 | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
若要返回序列或数组,则把需要返回的序列赋值目标当作参数传递给DLL,在DLL内部直接修改已达到赋值。 | 若要返回序列或数组,则把需要返回的序列赋值目标当作参数传递给DLL,在DLL内部直接修改已达到赋值。 | ||
注意逐根运行模式下,每一根K线都会调用dll,所以通常每次调用只需要赋值比上一根K线新增加的类型, | 注意逐根运行模式下,每一根K线都会调用dll,所以通常每次调用只需要赋值比上一根K线新增加的类型, | ||
例如计算收盘价均值时,每次在BarPos位置填入当根K线的均值即可。由此可见,这种应用下通常需要把 | 例如计算收盘价均值时,每次在BarPos位置填入当根K线的均值即可。由此可见,这种应用下通常需要把 | ||
− | BarPos(当前是第几根K线) | + | BarPos(当前是第几根K线)作为参数传入Dll中。返回序列和数组可以参考上面传入序列的公式例子。 |
− | + | ||
− | extern | + | 下面一个例子是在图形上输出当前是第几根K线的例子: |
− | + | ||
− | + | // 公式中的声明与调用 | |
− | + | extern 'FoxFunc.dll' void test9(NumericSeries, int); | |
− | extern "C" __declspec(dllexport) void | + | MyIndi:0; |
+ | test9(MyIndi, BarPos); | ||
+ | |||
+ | // c++ .h 声明文件 | ||
+ | __declspec(dllexport) void __stdcall test9(float *pf, int n); | ||
+ | |||
+ | // c+= .cpp 实现 | ||
+ | extern "C" __declspec(dllexport) void __stdcall test9(float *pf, int n) | ||
{ | { | ||
− | + | // 每根都会调用一次,所以每次只需赋值当前根 | |
− | + | pf[n - 1] = n; | |
} | } | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | = | + | =DLL的参数类型参考= |
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
− | ! | + | !- 类型 !! 公式 !! DLL |
+ | |- | ||
+ | | 数值 || NumericSimple || float, int, char, bool | ||
+ | |- | ||
+ | | 字符串 || LPCWSTR || wchar_t* | ||
+ | |- | ||
+ | | 数值引用 || NumericRef || float* | ||
|- | |- | ||
− | | | + | | 序列 || NumericSeries || float* |
|- | |- | ||
− | | | + | | 数组 || NumericArray || float* |
|} | |} | ||
第138行: | 第274行: | ||
k1:Invalid; | k1:Invalid; | ||
则k1指标线不会输出 | 则k1指标线不会输出 | ||
− | |||
− | |||
− | |||
第168行: | 第301行: | ||
这里的RzRq就是命名空间,而Grabber则是类的名称,GetFinance是函数名 | 这里的RzRq就是命名空间,而Grabber则是类的名称,GetFinance是函数名 | ||
+ | |||
+ | 另外,在.net DLL中,除了可以用参数来传递数据,还可以使用GetVarData, SetVarData这两个函数在.net和公式引擎间交换数据。 | ||
+ | |||
+ | .net DLL开发过程如下:<br> | ||
+ | 1. visual studio 新建一个类库 工程,用C#或者VB.net,Managed C++都可以,这里以C#为例<br> | ||
+ | [[image:dotnet1.png]]<br> | ||
+ | <br> | ||
+ | 2.添加 金魔方 .net api dll 引用,位置位于金魔方安装目录下 DotNetBridge.dll<br> | ||
+ | [[文件:dotnet2.png]]<br> | ||
+ | [[文件:dotnet3.png]]<br> | ||
+ | <br> | ||
+ | 3.新建或者修改 从 Formula 继承 一个类,Formula 是金魔方.net api的类,从此类继承可以访问 GetVarData等接口函数,需要用 fox.api 命名空间<br> | ||
+ | [[文件:dotnet4.png]]<br> |
2016年11月14日 (一) 14:54的最后版本
目录 |
[编辑] 简介
相比传统DLL接口(飞狐接口),自由DLL接口的特点是参数更加灵活,由用户指定传入的参数,参数类型是c/c++的标准数据类型。
传统DLL必须引入一个CalcInfo的结构化参数,通过它来传入行情数据,这种设计的好处是用户操心得少一些,因为数据总是有的,不利之处是限制了发挥,难以返回多个序列结果,难以接入没有按接口要求编写的DLL。
自由DLL接口,不再传入CalcInfo参数,取而代之的是,用户必须自己负责传入所有的数据。获得的回报是,
1.支持任意多的参数。
2.支持引用参数,也就是说,可以改变参数的值并返回给公式系统。
3.更灵活的参数传递。例如,可以根据需要传入地址,也可以传入值。
4.支持逐根的计算。每次只计算一根的值并返回单值。自由DLL只能在逐根模式下运行。
与传统接口DLL一样,自由接口DLL也必须放置在FmlDLL目录下。
[编辑] 自由DLL声明
为了让公式系统知道DLL的参数格式,调用前需要使用extern语句进行声明
extern 返回值类型 函数名(参数类型1, 参数类型2...);
参数的个数没有限制
[编辑] 参数类型
传入DLL的参数有类型限制,目前支持的C类型包括 float, double, int,BOOL, LONG, DWORD, float*, LPCWSTR等 不支持double *
[编辑] 返回值类型
函数可以返回 int, float, char, bool 等类型,或无返回。
若要在DLL内修改一条序列或者修改整个数组,可以通过传序列参数或数组参数的方法,在DLL里面赋值。DLL 里面都使用 float * 来接收参数。
[编辑] 自由DLL接口的调用
- 声明过的函数,可以通过函数名直接调用,无需通过"DLLName@FuncName"格式或RefDLL使用。
- 使用时必须将DLL放在软件安装目录下的FmlDll子目录下
- 字符串使用单引号,双引号是另一种格式的引用,不能混用
- DLL 需要在 .def 文件中 EXPORTS 项添加要调用的函数,否则有可能找不到函数
- DLL名称和函数名都是大小写敏感的,代码声明和公式调用时需要匹配。
[编辑] DLL公式参数传递例子
- 无参数时的例子:
// 公式里面的声明与调用 extern 'FoxFunc.dll' void test1(); test1();
// c++ .h 函数声明 __declspec(dllexport) void __stdcall test1();
// c++ .cpp 函数实现 extern "C" __declspec(dllexport) void __stdcall test1() { }
- 传入单值的公式例子:
// 公式里面的声明与调用 extern 'FoxFunc.dll' void test2(int, float, bool, char); Variables: NumericSimple var1, NumericSeries var2, NumericArray var3[10]; var1 := 1; var2[0] := 2; var3[0] := 65; test2(1, 2, 1, 65); test2(var1, var2[0], var1, var3[0]);
// c++ .h 函数声明 __declspec(dllexport) void __stdcall test2(int i, float f, bool b, char c);
// c++ .cpp 函数实现 extern "C" __declspec(dllexport) void __stdcall test2(int i, float f, bool b, char c) { }
- 传入字符串的公式例子
公式里面的字符串变量是宽字符,编码是GB2312,DLL里面可以使用MultiByteToWideChar/MultiByteToWideChar进行字符转换
// 公式里面的声明与调用 extern 'FoxFunc.dll' void test3(LPCWSTR); Variables: String var1; var1 := 'test string1'; test3(NumToStr(close,3)); test3('aaa'); test3(var1);
// c++ .h 函数声明 __declspec(dllexport) void __stdcall test3(const wchar_t* p);
// c++ .cpp 函数实现 #include <string> // 窄字符转宽字符 static std::wstring a2w(LPCSTR lpa) { size_t l = MultiByteToWideChar(936, 0, lpa, -1, NULL, 0); std::wstring s; s.resize(l - 1); MultiByteToWideChar(936, 0, lpa, (int)strlen(lpa), (LPWSTR)s.c_str(), (int)s.size()); return s; } // 宽字符转窄字符 static std::string w2a(LPCWSTR lpw) { size_t l = WideCharToMultiByte(936, 0, lpw, -1, NULL, 0, NULL, NULL); std::string s; s.resize(l - 1); WideCharToMultiByte(936, 0, lpw, (int)wcslen(lpw), (LPSTR)s.c_str(), (int)s.size(), NULL, NULL); return s; } extern "C" __declspec(dllexport) void __stdcall test3(const wchar_t *p) { std::wstring sw = p; std::string sa = w2a(sw.c_str()); }
- 传入序列的公式例子
// 公式声明与调用 extern 'FoxFunc.dll' void test4(NumericSeries, int, NumericArray, int); Variables: NumericSeries var1, NumericArray var2[5]; var1 := close; var2[1] := 1; var2[2] := 2; var2[3] := 3; var2[4] := 4; var2[5] := 5; //var2 数组实际大小是5+1 test4(var1, BarsCount(close), var2, 6);
// c++ .h 声明 __declspec(dllexport) void __stdcall test4(float *pf1, int n1, float *pf2, int n2);
// c++ .cpp 函数实现 extern "C" __declspec(dllexport) void __stdcall test4(float *pf1, int n1, float *pf2, int n2) { }
- 传入引用的公式例子
// 公式声明与调用 extern 'FoxFunc.dll' void test5(NumericRef); Variables: NumericSimple var1; var1 := 3; test5(var1); comment(var1);
// c++ .h 声明 __declspec(dllexport) void __stdcall test5(float *pf);
// c++ .cpp 实现 extern "C" __declspec(dllexport) void __stdcall test5(float *pf) { *pf = 9; }
[编辑] DLL的返回值
新格式DLL可返回单值,序列和数组。单值类型大致分为两种:数值类型和字符串类型,可以通过函数返回值直接返回。
- 返回单值类型的c语言例子
// 公式声明与调用例子 extern 'FoxFunc.dll' int test6(); extern 'FoxFunc.dll' float test7(); extern 'FoxFunc.dll' LPCWSTR test8(); k1: test6(),OwnerScale; k2: test7(),OwnerScale; comment(test8);
// c++ .h 声明 __declspec(dllexport) int __stdcall test6(); __declspec(dllexport) float __stdcall test7(); __declspec(dllexport) wchar_t* __stdcall test8();
// c++ .cpp 实现 extern "C" __declspec(dllexport) int __stdcall test6() { return 1; } extern "C" __declspec(dllexport) float __stdcall test7() { return 0.5f; } extern "C" __declspec(dllexport) wchar_t* __stdcall test8() { return L"test string 1"; }
- 返回序列或数组
若要返回序列或数组,则把需要返回的序列赋值目标当作参数传递给DLL,在DLL内部直接修改已达到赋值。 注意逐根运行模式下,每一根K线都会调用dll,所以通常每次调用只需要赋值比上一根K线新增加的类型, 例如计算收盘价均值时,每次在BarPos位置填入当根K线的均值即可。由此可见,这种应用下通常需要把 BarPos(当前是第几根K线)作为参数传入Dll中。返回序列和数组可以参考上面传入序列的公式例子。
下面一个例子是在图形上输出当前是第几根K线的例子:
// 公式中的声明与调用 extern 'FoxFunc.dll' void test9(NumericSeries, int); MyIndi:0; test9(MyIndi, BarPos);
// c++ .h 声明文件 __declspec(dllexport) void __stdcall test9(float *pf, int n);
// c+= .cpp 实现 extern "C" __declspec(dllexport) void __stdcall test9(float *pf, int n) { // 每根都会调用一次,所以每次只需赋值当前根 pf[n - 1] = n; }
[编辑] DLL的参数类型参考
- 类型 | 公式 | DLL |
---|---|---|
数值 | NumericSimple | float, int, char, bool |
字符串 | LPCWSTR | wchar_t* |
数值引用 | NumericRef | float* |
序列 | NumericSeries | float* |
数组 | NumericArray | float* |
[编辑] 无效数据表示
公式系统使用std的无效浮点数表示无效值,浮点无效值的一个重要特征是 f != f 为真,可作为判断依据, 其它运算皆输出无效值,其它比较皆输出假。当序列作为指标输出时,开始和结束两段的无效值自动截除,不作为指标输出。 例如要输出5日均线,序列的前4个值可填充无效值(填充0时会使指标线变形)
#include <limits> #define INVALID_NUMERIC std::numeric_limits<float>::quiet_NaN()
无效值在公式里可以用 Invalid 关键字指定,例如
k1:Invalid;
则k1指标线不会输出
[编辑] 编写调用公式
[编辑] 开发DLL的详细例子
[编辑] 实例下载
[编辑] 调用Windows系统DLL
调用win32提供的系统DLL,与调用自己编写的DLL是一样的。
只要知道windows系统函数所属的DLL和函数名,参数等信息就可以直接使用,例如
extern 'kernel32.dll' int GetTickCount(); tickcount:GetTickCount();
[编辑] 调用.Net DLL
跟一般DLL不同的是,.net DLL在声明时,除了函数名信息,还要提供命名空间、类名的信息,例如:
extern 'RzRq.dll' void RzRq.Grabber.GetFinance( LPCWSTR stockCode);
这里的RzRq就是命名空间,而Grabber则是类的名称,GetFinance是函数名
另外,在.net DLL中,除了可以用参数来传递数据,还可以使用GetVarData, SetVarData这两个函数在.net和公式引擎间交换数据。
.net DLL开发过程如下:
1. visual studio 新建一个类库 工程,用C#或者VB.net,Managed C++都可以,这里以C#为例
2.添加 金魔方 .net api dll 引用,位置位于金魔方安装目录下 DotNetBridge.dll
3.新建或者修改 从 Formula 继承 一个类,Formula 是金魔方.net api的类,从此类继承可以访问 GetVarData等接口函数,需要用 fox.api 命名空间