【问题标题】:How to create a file in delphi pass it to a C DLL and use the file in a callback?如何在delphi中创建文件将其传递给C DLL并在回调中使用该文件?
【发布时间】:2019-11-05 08:25:51
【问题描述】:

在 ExportToFileCallback 中,我想使用我在 SaveMsg 中分配的文件,但我似乎无法从指向 FilePCDMsg 的指针中获取它。

type
  FilePCDMsg  = File of PCDKMsg;    
  PFilePCDMsg = ^FilePCDMsg;


procedure TfrmMain.SaveMsg( pMsg: PCDKMsg; fn: String = '' );
var
  myFile: FilePCDMsg;
  pFile : PFilePCDMsg;
begin
  if fn.IsEmpty then
    fn := TPath.GetTempFileName;
  CodeSite.Send( 'SaveMsg fn', fn );

  AssignFile( myFile, fn );
  Rewrite( myFile );
  pFile := Addr( myFile );
  CDKMsgExport( pMsg, ExportToFileCallBack, pFile );
end;

function ExportToFileCallBack( pData: PUInt8; len: UInt32; pUser: Pointer ): Integer; cdecl;
var
  pFile : PFilePCDMsg;
  myFile: FilePCDMsg;
begin
  // this is the C code for what I want to do
  // return fwrite(pData, 1, len, (FILE*)pUser) == len;
  pFile  := PFilePCDMsg( pUser );
  myFile := FilePCDMsg( pFile ); // will not compile
end;

这个 DLL 是由第三方提供的,它有回调来做事。在这里,我试图查询相机的配置,并且回调被调用了 3 次。我认为它正在将它希望我附加到我分配的文件中的数据传回给我。

这是我试图在 Delphi 中复制的 C 代码。

#ifdef WIN32
#include <windows.h>
#include "../../include/external/stdint.h"
#include "../../include/external/inttypes.h"
#endif

#ifdef LINUX
#include <stdlib.h> 
#include <unistd.h>
#include <stdint.h>
#include <inttypes.h>
#endif

#include <stdio.h>


#include "../../include/CDK.h"

const char* strAddress = NULL;
uint16_t uPort = 10001;
const char* strOptions = NULL;
const char* strPathOut = ".";


void help()
{
    printf("Command line:\n");
    printf("   receive address [-port port] [-options options] [-output outputpath]\n");
    printf("   Port default value : 10001\n");
    printf("   Output path default value : .\n");
}

int parseCommandLine(int argc, char* argv[])
{
    int i;

    if (argc < 2)
    {
        return 0;
    }

    strAddress = argv[1];

    for (i=2;i<argc;++i)
    {
        if (strcmp(argv[i], "-port") == 0)
        {
            ++i;
            if (i >= argc)
                return 0;
            uPort = atoi(argv[i]);
        }
        else if (strcmp(argv[i], "-options") == 0)
        {
            ++i;
            if (i >= argc)
                return 0;
            strOptions = argv[i];
        }
        else if (strcmp(argv[i], "-output") == 0)
        {
            ++i;
            if (i >= argc)
                return 0;
            strPathOut = argv[i];
        }
    }

    return 1;
}



void traceCallback(CDK* pSrc, unsigned char level, const char* strTrace, void* pUser ) 
{ 
    printf("[%u] %s\n", level, strTrace);
}

int32_t exportToFileCallback(const uint8_t* pData, uint32_t len, void* pUser)
{
    return fwrite(pData, 1, len, (FILE*)pUser) == len;      
}

void parseConfig(CDKMsg *pMsg)
{
    CDKMsgElement* pEltConfig = NULL;
    CDKMsgElement* pEltDevice = NULL;
    CDKMsgElement* pEltCameras = NULL;
    const char* str;
    uint32_t i = 0;
    uint32_t j = 0;


    pEltConfig = CDKMsgChild(pMsg);
    if(NULL == pEltConfig)
    {
        return;
    }
    pEltDevice = CDKMsgElementFirstChild(pEltConfig, "device");
    if (NULL != pEltDevice )
    {
        str = CDKMsgElementAttributeValue(pEltDevice, "name");
        if (str)
            printf("Name : %s\n", str);
    }
    pEltCameras = CDKMsgElementFirstChild(pEltConfig, "cameras");
    if (NULL != pEltCameras )
    {
        CDKMsgElement* pEltCamera = CDKMsgElementFirstChild(pEltCameras,NULL);
        CDKMsgElement* pEltCameraParam = NULL;
        while(pEltCamera)
        {
            /* camera attributes */
            printf("%s : \n", CDKMsgElementName(pEltCamera));
            for(i = 0; i < CDKMsgElementAttributeCount(pEltCamera); i++)
            {
                str = CDKMsgElementAttributeName(pEltCamera,i);
                printf("* \t %s : %s\n", str,CDKMsgElementAttributeValue(pEltCamera,str));
            }

            /* camera parameters */
            pEltCameraParam = CDKMsgElementFirstChild(pEltCamera,NULL);
            while(pEltCameraParam)
            {
                printf("* \t %s : \n", CDKMsgElementName(pEltCameraParam));

                for(j = 0; j < CDKMsgElementAttributeCount(pEltCameraParam); j++)
                {
                    str = CDKMsgElementAttributeName(pEltCameraParam,j);
                    printf("** \t\t %s : %s\n", str,CDKMsgElementAttributeValue(pEltCameraParam,str));
                }

                pEltCameraParam = CDKMsgElementNextChild(pEltCamera,pEltCameraParam,NULL);
            }

            pEltCamera = CDKMsgElementNextChild(pEltCameras,pEltCamera,NULL);
        }


    }
}

int main(int argc, char* argv[])
{
    CDK* pCDK = NULL;
    FILE* pFile = NULL;
    char strFileName[256];
    CDKMsg * pMsgRequest = NULL;
    CDKMsg * pMsgAnswer = NULL;

    CDKSetTraceFunction(traceCallback, NULL);

    printf("App launched\n");
    printf("CDK version : %s\n", CDKGetVersion()); 

    /* command line */
    if (!parseCommandLine(argc, argv))
    {
        help();
        return -1;
    }

    printf("Get config from %s:%u in %s\n", strAddress, uPort, strPathOut);
    if (strOptions)
    {
        printf("   Options : %s\n", strOptions);
    }

    pCDK = CDKCreate();
    if (!CDKBind(pCDK, strAddress, uPort, strOptions))
    {
        printf("CDKBind failed : %s\n", CDKGetLastError(pCDK));
        goto labelEnd;
    }
    if(!CDKWaitForConnection(pCDK,30000))
    {
        printf("ERROR : %s\n", CDKGetLastError(pCDK));
        goto labelEnd;
    }

    /* get config */
    pMsgRequest = CDKMsgCreate();
    CDKMsgSetChild(pMsgRequest, CDKMsgElementCreate("getConfig"));

    pMsgAnswer = CDKSendRequest(pCDK, pMsgRequest, 30000);
    CDKMsgDestroy(pMsgRequest);
    if (NULL == pMsgAnswer )
    {
        printf("ERROR : %s\n", CDKGetLastError(pCDK));
        goto labelEnd;
    }

    /* export config msg */
    sprintf(strFileName, "%s/config.cdkmsg", strPathOut);
    pFile = fopen(strFileName, "wb");
    if (NULL == pFile)
    {
        printf("Unable to create output file, does the output path exist?");
        goto labelEnd;
    }
    if (!CDKMsgExport(pMsgAnswer, &exportToFileCallback, pFile))
    {
        printf("Message export failed : %s", CDKGetLastError(pMsgAnswer));
        goto labelEnd;
    }

    /* Parse config */
    parseConfig(pMsgAnswer);



labelEnd:

    if (pFile)
    {
        fclose(pFile);
    }
    if (pMsgAnswer)
    {
        CDKMsgDestroy(pMsgAnswer);
    }
    if (pCDK)
    {
        CDKDestroy(pCDK);
    }   

    return 0;


}

【问题讨论】:

    标签: c delphi dll


    【解决方案1】:

    我想多了。答案其实很简单

    procedure TfrmMain.SaveMsg(pMsg: PCDKMsg);
    begin
      CDKMsgExport( pMsg, ExportToFileCallBack, nil );
    end;
    
    function ExportToFileCallBack( pData: PUInt8; len: UInt32; pUser: Pointer ): Integer; cdecl;
    var
      FileName: string;
      Stream  : TStream;
    begin
      FileName := 'Config.xml';
      if TFile.Exists( FileName ) then
        Stream := TFileStream.Create( FileName, fmOpenReadWrite )
      else
        Stream := TFileStream.Create( FileName, fmCreate );
      try
        Stream.Seek( 0, soFromEnd );
        Stream.WriteBuffer( Pointer( pData )^, len );
        CodeSite.SendStreamAsText( 'ExportToFileCallBack', Stream );
      finally
        Stream.Free;
      end;
    end;
    

    非常感谢你帮助我

    【讨论】:

    • if TFile.Exists( FileName ) then 毫无意义。和一个错误。如果在您调用TFile.Exists 之后但在您尝试打开文件之前删除了该文件,则打开尝试将失败。无条件使用fmCreate
    • 这不是没有意义的,回调至少被调用了 3 次。您不会一次获得所有数据。
    • 还不错。正如我所描述的,您仍然有竞争条件。解决这个问题的正确方法是不要使用TFile.Exists。正确的方法是在调用CreateFile 时使用OPEN_ALWAYS 创建处置。不幸的是 TFileStream 不提供该功能。当我这样做时,我使用THandleStream 调用我控制的CreateFile,因此可以使用OPEN_ALWAYS
    【解决方案2】:

    在程序 TfrmMain.SaveMsg 中:

    myFile: FilePCDMsg;是一个本地变量(保存在堆栈上)。

    因此,当 TfrmMain.SaveMsg 执​​行完成时,文件引用 myFile 将不再有效,因此指针 您传递给 CDKMsgExport() 的 pFile 将变得陈旧!

    这是最大的问题!

    一个小得多的问题是语法:

    myFile := FilePCDMsg( pFile ); // 不会编译

    我的文件 := pFile^; // 应该编译

    如果在 TfrmMain.SaveMsg 执​​行完成后需要文件引用,请勿使用局部变量。

    全局变量可以使用,但您必须注意不要在它仍然具有以前的和需要的值时覆盖它。

    其他(可能更好)的想法是在堆上分配文件变量:使用 New() 然后传递一个指向它的指针。

    但是,如果您这样做,则 ExportToFileCallBack() 还应该 Dispose() 分配的指针。

    回调被调用了 3 次。

    如果您的意思是 ExportToFileCallBack() 将在您之后调用 3 次 只调用一次 TfrmMain.SaveMsg,

    这意味着更多的麻烦!

    如果你第一次 Dispose() 分配的指针,那么指针在第二次和第三次当然将不再有效!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-01
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      相关资源
      最近更新 更多