简体   繁体   中英

How to create a file in delphi pass it to a C DLL and use the file in a callback?

In ExportToFileCallback I want to use the file I assigned in SaveMsg but I don't seem to be able to get it from the pointer back to 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;

This DLL is provided by a third party and it has callbacks to do things. Here I am trying to query a camera for its configuration and the call back is called three times. I think it is passing back to me the data it wants me to append to the file I have assigned.

Here is the C code I am trying to replicate in Delphi.

#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;


}

I was overthinking it. The answer is in fact quite simple

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;

Many thanks for helping me out

in procedure TfrmMain.SaveMsg:

the myFile: FilePCDMsg; is a LOCAL variable (saved on stack).

Therefore, when execution of TfrmMain.SaveMsg finishes, the file reference myFile will no longer be valid, and therefore the pointer pFile you passed to CDKMsgExport() will become stale !

This is the biggest problem !

A much smaller problem is the syntax:

myFile:= FilePCDMsg( pFile ); // will not compile

myFile:= pFile^; // should compile

If you need the file reference AFTER execution of TfrmMain.SaveMsg has finished, DO NOT use a local variable.

A global variable will work, but you must be careful that you do not overwrite it while it still has a previous and needed value.

Other (and possibly better) idea is to allocate the file variable on heap: use New() and then pass a pointer to it.

However, if you do this, then the ExportToFileCallBack() should also Dispose() the allocated pointer.

the call back is called three times.

If you mean that the ExportToFileCallBack() will be called 3 times after you call TfrmMain.SaveMsg only one time,

This means more trouble !

IF you Dispose() the allocated pointer in the first time, then of course the pointer will no longer be valid in the 2nd and 3rd time !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM