FILE指针在两个dll中无法正确操作的问题

int main(int argc, char* argv[])
{
    FILE * stream;
    if( (stream=fopen("F://1.txt","w+")) !=NULL )
    {
        printf("fp1=%X/n", stream);
        testFile( stream );
    }
    return 0;
}

//dll中
void testFile(FILE *fp)
{
    int xx=1234;
    fread(&xx,sizeof(int),1,fp);   
}

这样出错, 提示00000010地址不能为写!

 

先解释几个基本概念:

1,重复定义

出现变量或者函数重复定义,VC6一般报这样一个错误:fatal error LNK1169: one or more multiply defined symbols found

这个错误表明某个符号在工程中被定义了两份。这种情况一般发生在,某个变量在多个源文件(注:区别于头文件)出现定义。


2,工程间源文件分享

这种分享有两种方式,一种是通过.h文件单独分享,一般情况下,这种分享是安全的。

另一种是通过.h + .lib,或者 .h + .c,.cpp共享,这时候就会发生所谓的“隐式重复定义”。


观察问题中的结构,可以发现问题的起因就是FILE *指针的共享,那么在fopen,fwrite之类的函数中,到底对FILE *做了些什么呢?

调试这个源代码,可以在

_file.c

文件中跟踪到如下代码:
void __cdecl _lock_file (
        void *pf
        )
{
        /*
         * The way the FILE (pointed to by pf) is locked depends on whether
         * it is part of _iob[] or not
         */
        if ( (pf >= (void *)_iob) && (pf <= (void *)(&_iob[_IOB_ENTRIES-1])) )
            /*
             * FILE lies in _iob[] so the lock lies in _locktable[].
             */
            _lock( _STREAM_LOCKS + ((FILE *)pf - _iob) );
        else
            /*
             * Not part of _iob[]. Therefore, *pf is a _FILEX and the
             * lock field of the struct is an initialized critical
             * section.
             */
            EnterCriticalSection( &(((_FILEX *)pf)->lock) );
}

这是在fread中调用到的一个函数,注意函数中的这句:

if ( (pf >= (void *)_iob) && (pf <= (void *)(&_iob[_IOB_ENTRIES-1])) )

这里和 pf 比较的变量 _iob 是唯一没有跟随FILE * (pf)一起传递的变量,那么问题是否就出在这里呢?

在这个文件中,找到了 _iob的定义:

FILE _iob[_IOB_ENTRIES] = {
        /* _ptr, _cnt, _base, _flag, _file, _charbuf, _bufsiz */

        /* stdin (_iob[0]) */

        { _bufin, 0, _bufin, _IOREAD | _IOYOURBUF, 0, 0, _INTERNAL_BUFSIZ },

        /* stdout (_iob[1]) */

        { NULL, 0, NULL, _IOWRT, 1, 0, 0 },

        /* stderr (_iob[3]) */

        { NULL, 0, NULL, _IOWRT, 2, 0, 0 },

};


这样一来,问题就清楚了。

_file.c是一个公共源文件,工程exe和dll分别引用了这个文件。

于是在exe中和dll中,这个变量_iob就存在了两份(隐式重复定义),而这个变量是影响相关函数执行结果的一个变量。(这个问题也会出现在使用lib作为公共源文件的场合)

在exe中,调用fopen,fread等函数的时候,内部调用的是链接进exe文件的_iob,而dll的导出接口调用的,是链接进dll内部的同名变量。

问题发生了,exe中的改变并不会影响dll中同名变量的数值,那么当在exe中想当然的认为正确的数值,到了dll中就面目全非了,结果必然是出错。

 

解决这个问题的方法有四个:

1,修改DLL,把所有相关的函数,包括fopen,fread,fwite等全部导出。

这样做可以确保所有调用引用的_iob都指向DLL内部的版本,但是由于涉及的函数比较多,操作复杂,并且影响DLL外部接口的清晰。


2,修改程序逻辑,把传递FILE * 改为传递自定义的文件缓冲区指针。

这样可以完全避免问题,同时程序的接口也将更简单。


3,修改_file.c及相关库的实现。

这么作的好处是一劳永逸,并且也为其他库的使用者避免了麻烦(当然,用户还是需要更新库文件并且重新编译链接),只是这个恐怕需要库供应商的支持,并且是否可行还有待商榷。


FILE *不允许在exe和DLL之间作为接口参数传递

查阅了msdn,并没有找到相关的描述。

不过相信在其他库中,应该还存在其他的类似问题,这里友情提醒大家在编码的时候多加留意。

 

4.可以把工程的C++ library设成(Debug) Multithread DLL版本就不会有这个问题了。

原本只修改了工程的,所以问题依旧,现在两边都修改就没有问题了。


--------------------next---------------------
不行么?想不通阿,据我的经验这是明显的两份crt的问题,由于静态联编crt导致操作FILE的函数在实际运行时有exe中的和dll中的两份,又,众所周知crt依赖于一些全局的数据,两份crt必然有两份数据,比如FILE和HANDLE的对应关系表之类的,用其中一份打开一个文件,只能在一处留下记录,调用另一处的fread,这个FILE*肯定是非法的

解决方法一楼小明说得有理,不是利用mt,而是要那个dll,就是动态联编crt,于是无论如何,进程中只会有一个msvcrt.dll,一些需要的全局数据也只有一份了

类似的比如exe和dll传递c++对象的也可用类似方法解决

此法敝人屡试不爽,不知为何这次不行,着实想不通

不知楼主是否exe和dll都是动态联编crt的,仅仅一边动态联编也是不行的
--------------------next---------------------

<think>首先,用户的问题是关于通达信中两个DLL函数的功能和使用方法。用户说:"我想了解通达信中两个dll函数的功能和使用方法 请问通达信 两个dll函数的作用及使用示例"。但用户没有指定是哪两个DLL函数。所以,我需要基于系统指令和提供的引用来回答。 回顾系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户可见层要求:回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 提供的引用是关于通达信DLL开发的: - 引用[1]:提到通达信DLL开发通用包,包含基础方法如移动平均线(MA)。 - 引用[2]:讨论DLL函数参数错误的问题,给出了一个示例函数`TestPlugin`。 - 引用[3]:展示了另一个DLL函数`TestPlugin1`,它写入数据到文件。 - 引用[4]:关于C语言编制DLL文件。 - 引用[5]:Python调用通达信函数。 用户提到了"两个dll函数",但没有具体名字。在引用中,有两个示例函数: 1. `TestPlugin`:在引用[2]中,它是一个简单的加法函数。 2. `TestPlugin1`:在引用[3]中,它写入数据到文件。 我应该基于这些来回答,因为用户的问题可能指的是这些通用示例。或者,我需要假设用户是指常见的DLL函数,如移动平均线(MA)或其他。 在引用[1]中,提到了"基础且常用的方法实现",如移动平均线(MA)。所以,我可以选择两个常见的函数:一个是简单的计算函数(如加法),另一个是更复杂的(如MA或文件操作)。 为了结构清晰,我将逐步解释: 1. 介绍DLL函数在通达信中的作用。 2. 描述两个示例函数:一个简单计算函数和一个文件操作函数。 3. 提供使用示例。 4. 添加引用标识。 5. 生成相关问题。 回答结构: - 开头解释DLL函数在通达信中的用途。 - 然后,介绍第一个函数:例如,`TestPlugin`(简单加法)。 - 提供代码示例和使用方法。 - 介绍第二个函数:例如,`TestPlugin1`(写入文件)。 - 提供代码示例和使用方法。 - 结束时生成相关问题。 确保使用中文。 数学表达式:用户的问题没有涉及数学,但如果需要,我可以添加一些。例如,在解释计算时,使用行内数学表达式。但问题主要是编程相关的,所以可能不需要太多数学。如果需要,比如在描述算法时。 在引用[2]的函数中,有加法计算:$pfOUT[i] = pfINA[i] + pfINB[i] + pfINC[i]$。我可以使用数学格式。 独立公式:如果有公式,单独成段。 现在,构建回答。 首先,标题或开头:由于用户问"两个dll函数的作用及使用示例",我将直接回答。 步骤: 1. **解释DLL函数在通达信中的角色**:DLL函数允许开发者扩展通达信的功能,自定义指标或计算。 2. **第一个函数示例**:基于引用[2],`TestPlugin`函数。 - 作用:执行简单的加法计算。 - 使用示例:在通达信公式中调用。 3. **第二个函数示例**:基于引用[3],`TestPlugin1`函数。 - 作用:写入数据到文件。 - 使用示例:代码和调用方式。 4. **注意事项**:基于引用,提醒参数类型和调试方法。 5. **生成相关问题**:在末尾添加。 引用标识:在引用内容后添加[^数字]。 例如,在描述函数时,说"如引用[2]所示",然后添加[^2]。 最终,生成相关问题: - 类似示例中的快速排序问题。 相关问题: 1. 如何调试通达信DLL函数? 2. 通达信DLL开发的最佳实践是什么? 3. 如何优化通达信DLL函数的性能? 现在,写回答。</think>### 通达信DLL函数的作用及使用示例 在通达信软件中,DLL(动态链接库)函数允许开发者通过自定义C/C++代码扩展软件功能,例如实现复杂的金融指标计算、数据处理或文件操作。这些函数需遵循通达信的编程规范,并通过公式管理器调用。以下基于常见开发场景,介绍两个典型的DLL函数示例:一个用于基础数值计算(如加法),另一个用于文件操作(如写入数据)。每个示例均提供功能说明、代码实现及调用方法。所有示例代码基于通达信DLL开发规范[^1][^4]。 --- #### 1. **基础数值计算函数:`TestPlugin`** - **作用**:此函数执行简单的数值运算,例如计算三个输入数组的和。它常用于测试DLL集成或实现自定义指标(如移动平均线)。函数原型需匹配通达信规范:参数包括数据长度(`DataLen`)、输出数组(`pfOUT`)和输入数组(`pfINA`, `pfINB`, `pfINC`)。计算过程可表示为行内数学表达式:$pfOUT[i] = pfINA[i] + pfINB[i] + pfINC[i]$[^2]。 - **使用示例**: - **DLL代码实现(C++)**: ```cpp #include "TCalcFuncSets.h" // 通达信头文件 void TestPlugin(int DataLen, float* pfOUT, float* pfINA, float* pfINB, float* pfINC) { for (int i = 0; i < DataLen; i++) { pfOUT[i] = pfINA[i] + pfINB[i] + pfINC[i]; // 计算每个元素的累加和 } } ``` - 编译此代码为DLL文件(如`CalcFunc.dll`),并放置于通达信安装目录的`T0002/dlls/`文件夹下[^1][^3]。 - **通达信公式调用**: - 在公式管理器中绑定DLL,使用`TDXDLL1`函数调用: ``` // 计算收盘价(C)、最高价(H)、最低价(L)三者之和 OUTPUT := TDXDLL1(1, C, H, L); ``` - 此处`1`对应DLL中的函数索引(需在通达信中注册),`C`、`H`、`L`为K线数据数组[^2]。 - **注意事项**:确保输入参数类型正确。例如,传递常量(如`TDXDLL1(1, 20, H, L)`)会导致错误,因为`20`被视为指针而非数值[^2]。调试时,可使用Visual Studio附加到通达信进程检查内存值。 #### 2. **文件操作函数:`TestPlugin1`** - **作用**:此函数将计算结果写入本地文件,便于调试或数据持久化。例如,记录指标计算结果到文本文件。函数同样遵循通达信规范,但添加了文件I/O操作[^3]。 - **使用示例**: - **DLL代码实现(C++)**: ```cpp #include "TCalcFuncSets.h" #include <fstream> // 文件操作库 void TestPlugin1(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc) { for (int i = 0; i < DataLen; i++) { pfOUT[i] = i; // 输出索引值(示例功能) } // 写入数据到文件 std::ofstream outFile; outFile.open("D:\\Test.txt"); // 文件路径 if (outFile.is_open()) { for (int i = 0; i < 5; i++) { outFile << i << "\n"; // 写入示例数据(如1,2,3,4,5) } outFile.close(); } } ``` - 编译后,将DLL文件放入`T0002/dlls/`目录[^3]。 - **通达信公式调用**: ``` // 调用函数并写入文件 FILE_OUT := TDXDLL1(2, C, 0, 0); // 索引2对应TestPlugin1,后两个参数可为0(未使用) ``` - 执行后,检查`D:\Test.txt`文件,内容应为写入的数值(如`1\n2\n3\n4\n5`)[^3]。 - **注意事项**:文件路径需有写入权限;避免在循环中频繁写文件以防性能下降。调试时,使用Process Explorer监控文件访问权限[^2]。 --- #### 关键注意事项 - **参数规范**:通达信DLL函数必须严格匹配参数类型(如`float*`数组)。错误传递常量或错误索引会导致输出乱码或崩溃[^2][^4]。 - **开发环境**:推荐使用Visual Studio编译DLL,并参考通达信开发包(包含`TCalcFuncSets.h`头文件)[^1][^4]。 - **性能优化**:避免密集I/O操作;优先使用数组计算以提高效率。数学优化如简化循环(时间复杂度$O(n)$)可提升速度[^1]。 通过以上示例,您可快速实现自定义功能。如需完整开发包,请参考通达信官方资源[^1]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值