1
votes

**

SOLVED

**

Thanks to the Rob for bringing light to my problem.

Create a global var on component unit fDict Implement 3 methods, one to add, one to remove and one to retrive; making the main class create , clear and destroy the global var to avoid memory leaks making the connectin function of TDispositivo to feed the fDict making the callback to retrive and move on. making the disconnect function of TDispositivo to remove the reference in fDict keeping it updated

**

EDIT

**

I found my crash error, i´m implementing the dll inside a component class and try to declare the callback procedures inside the class using

TSnapRev = procedure(XXXXX) of object stdcall;

... private procedure callbacksnapshot(XXXXX); stdcall; ...

CLIENT_SetSnapRevCallBack(callbackSnapshot,FSDKUSer);

the callbacksnapshot was triggered but raising a accesse violation after.

O realize the order of variables returned was always misplaced, then i change my approuch moving the callbacksnapshot procedure out of the class and changing type declaration like this:

Type TSnapRev = procedure(XXXXX); stdcall;

TDispositivo = class private end;

procedure callbacksnapshot(XXXXX); stdcall;

... CLIENT_SetSnapRevCallBack(@callbackSnapshot,FSDKUSer); ...

now the callback triggeing, the order of variables are right and no access violation after, BUT, i need to invoke some functions (events, etc) from my TDispositivo instance or from the owner of TDispositivo instance, in a outside procedure i can´t.

How can i do this into my class ?

i had tryed declare a variable FSDKSnap : TRevSap (when TRevSap is a type of object) but got the same erros or miplaced vars and access violations

**

ORIGINAL

**

Keep working on my Dahua dll convertion to Delphi. Now i'm stuck in a callback, actually in a definition of the type used.

There is the cpp code in header

    #define BYTE        unsigned char
    #define UINT        unsigned int

    // Snapshot parameter structure 
    typedef struct _snap_param
    {
        unsigned int        Channel;                // Snapshot channel
        unsigned int        Quality;                // Image quality:level 1 to level 6
        unsigned int        ImageSize;              // Video size;0:QCIF,1:CIF,2:D1
        unsigned int        mode;                   // Snapshot mode;0:request one frame,1:send out requestion regularly,2: Request consecutively
        unsigned int        InterSnap;              // Time unit is second.If mode=1, it means send out requestion regularly. The time is valid.
        unsigned int        CmdSerial;              // Request serial number
        unsigned int        Reserved[4];
    } SNAP_PARAMS, *LPSNAP_PARAMS;

    // Snapshot callback function original shape 
    // Encode Type 10: jpeg 0: number i frame of mpeg4 
    typedef void (CALLBACK *fSnapRev)(LLONG lLoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, DWORD CmdSerial, LDWORD dwUser);

    // Set snapshot callback function 
    CLIENT_NET_API void CALL_METHOD CLIENT_SetSnapRevCallBack(fSnapRev OnSnapRevMessage, LDWORD dwUser);    

    // Snapshot request
    CLIENT_NET_API BOOL CALL_METHOD CLIENT_SnapPicture(LLONG lLoginID, SNAP_PARAMS par);

Looking to the cpp code, it defined BYTE as unsigned char, so mapping to Delphi (Byte), UINT mapping to Cardinal.

The structure was mapped to Cardinal as well.

---> First question HERE

The callback fSnapRev has (BYTE *pBuf) so i suppose is a pointer to byte (Delphi PByte) and i suspect here is the problem. A buffer with so little memory is not right, maybe a array of bytes or array of pansichar. How can i do know that ? like try and miss ? I accept suggestions.

The RevLen and Encode Type was mapped to Cardinal.

There is the cpp code from demo application.

  //First part of the method to initialize the dll, 

    BOOL ret = CLIENT_Init(DisConnectFunc, (LDWORD)this);
    if (ret)
    {
        CLIENT_SetSnapRevCallBack(SnapPicRet,(LDWORD)this);
        //here defining the callback
        CLIENT_SetAutoReconnect(ReConnectFunc, (LDWORD)this);
    }
    else
    {
        MessageBox(ConvertString("initialize SDK failed!"), ConvertString("prompt"));
    }

    //the callback
    void CALLBACK SnapPicRet(LLONG ILoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, DWORD CmdSerial, 
    LDWORD dwUser)
    {
        CCapturePictureDlg *pThis = (CCapturePictureDlg*)dwUser;
        pThis->OnOnePicture(ILoginID,pBuf,RevLen,EncodeType,CmdSerial);
    }

    
    void CCapturePictureDlg::OnOnePicture(LLONG ILoginID, BYTE *pBuf, UINT RevLen, UINT EncodeType, UINT 
CmdSerial)
    {
        //Get file path
        char path[1000];
        int filelen = GetModuleFileName(NULL,path,1000);
        int i = filelen;
        while(path[i]!='\\')
        {
            i--;
        }
        path[i + 1] = '\0';
    
        //Get file name
        CString filepath(path);
        CString filename = "mpeg4.JPG";
        CString strfile = filepath + filename;
        char *pFileName = strfile.GetBuffer(200);
  
        /* Save image original file */
        FILE *stream;        
        if( (stream = fopen((const char*) pFileName, "wb")) != NULL )
        {
            int numwritten = fwrite( pBuf, sizeof( char ), RevLen, stream );
            fclose( stream );
        }
        
        /*Veirfy image encode type. If it is an I frame of mpeg4,then call I frame to decode to BMP to display.*/
        if ( EncodeType == 0) 
        {
            //int iRet = IFrameToBmp("tmp.bmp",pFileName);
            //if (iRet == 1)
            //{
            //  ShowBitmap("tmp.bmp");
            //}
        }
        else if (EncodeType == 10)
        {
            //ShowBitmap(pFileName);
            ShowSnapImage(pFileName);
        }
    }

In OnOnePicture the first code is to define the path to image file be stored, then write the buffer in a file stream. if the buffer and size comes from pBuf and RevLen, then i can write this on a TMemoryStream and assign to TJpegImage but still do not know what pBuf really is.

Another ocuard thing is the data, revLen returns value 10 and is too small to be a byte length of the image.

My Delphi Code

  TSnapRev = procedure(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD) of object stdcall;

// Snapshot parameter structure
  PSnapParams = ^TSnapParams;
  TSnapParams = record
    Channel: Cardinal;               // Snapshot channel
    Quality: Cardinal;               // Image quality:level 1 to level 6
    ImageSize: Cardinal;             // Video size;0:QCIF,1:CIF,2:D1
    mode: Cardinal;                  // Snapshot mode;0:request one frame,1:send out requestion regularly,2: Request consecutively
    InterSnap: Cardinal;             // Time unit is second.If mode=1, it means send out requestion regularly. The time is valid.
    CmdSerial: Cardinal;             // Request serial number
    Reserved: array[0..3] of Cardinal;
  end;

// Snapshot request
  function CLIENT_SnapPicture(lLoginID: Integer; par: TSnapParams): Boolean;  stdcall; external 'dhnetsdk.dll';

  procedure callbackSnapshot(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD); stdcall;

 //Part of my initDll function
   FInicializado := CLIENT_Init(callbackDis,FSDKUser);
  if FInicializado then
    begin
      CLIENT_SetSnapRevCallBack(callbackSnapshot,FSDKUSer);
    end;
   Result := FInicializado;

  //Snapshot wrapper procedure
  procedure TDispositivo.SDKSnapshot;
  var
    sIn: TSnapParams;
  begin
    ZeroMemory(@sIn, SizeOf(sIn));
    With sIn do
      begin
        Channel := 1;
        mode := 0;
        CmdSerial := 0;
        if not CLIENT_SnapPicture(FLoginHandler,sIn) then
          begin
            doErro(999,'Não foi possível tirar o snapshot');
          end;
      end;
  end; 

//the callback implementation
procedure TDispositivo.callbackSnapshot(lLoginID: Integer; pBuf: PByte; RevLen: Cardinal; EncodeType: Cardinal; CmdSerial: DWORD; dwUser: DWORD);
var
  F: TMemoryStream;
  jpg : TJpegImage;
begin
  Showmessage('Tamanho do buffer: ' + SizeOf(pBuf).ToString + #13 +
              'RevLen: ' + RevLen.ToString + #13 +
              'Tipo: ' + EncodeType.ToString + #13 +
              'CmdSerial: ' + CmdSerial.ToString
              );
  F := TMemoryStream.Create;
  jpg := TJPEGImage.Create;
  Try
    F.Write(pBuf,RevLen);
    //F.Write(pBuf^,RevLen);
    F.Position:= 0;
    jpg.LoadFromStream(F);
    fOwner.doSnapshot(self,jpg);
  Finally
    FreeAndNil(F);
    FreeAndNil(jpg);
  End;
end;  

When i call SDKSnapshot, i got the "showmessage" placed in callbackfunction with the values, but im debug mode i got infinite access violation, on Release mode the application close after the showmessage.

I try to do nothing on callback but still getting access violation and termination.

I´m wondering if my problem is the declaration of the callback procedure, pBuf.

2

2 Answers

2
votes

I am posting a different answer because this is a different question now.

Your callback is a standard procedure and does not accept an object reference (and can't be called as an object method which it wasn't when I checked your previous post as this would be one of the key reasons why variables don't match up).

You need to be able to look up the object from some information that you have that will be unique in the callback. Without knowing more details about the API you're calling it looks like that would either be LLoginId or dwUser in your case.

I would probably implement a TDictionary with a lookup to match the unique code to the object you're using. Remember to remove objects from the TDictionary when you're not using them!

Assuming you can use dwUser your code may be something like:

implementation

  uses Systm.Generics.Collections;

  var
    gClassDict:      TDictionary<DWORD, TDispositivo>;

  procedure RegisterInDictionary(dwUser: DWORD, class: TDispitivo);
  begin
    if(gClassDict=nil) then
      gClassDict:=TDictionary<DWORD, TDispositivo>.Create;
    gClassDict.AddOrSetValue(DWORD, class);
  end;

  function GetClassFromDictionary(dwUser: DWORD): TDispotivo;
  begin
    if( (gClassDict=nil) Or
        (not(gClassDict.TryGetValue(dwUser, Result))) ) then
      Result:=nil;
  end;

Then in your callback call GetClassFromDictionary to obtain your object.

1
votes

I suspect your LLoginId shoud be an Int64 .. LLONG is going to be defined in the compiler dialect / system includes for your source platform. (See: long long in C/C++)

Your errors indicate that the parameters are holding garbage (see below) which would happen if they are out of sequence - which probably means that you have the wrong parameter sizes (hence LLoginId not reading all of the value from the stack, all variables that follow it will be corrupted).

You can try making LLogin Int64, or if you want to invesitigate further you can try the following:

It seems that you are successfully calling the callback routine, but that the variables you receive in the routine are incorrect. (You say that you have revLen only 10, and the access violation errors indicate that pBuf is probably pointing at garbage).

You have declared the routine as stdcall which should be correct, and parameters will be passed on the stack in the normal "C" convention.

If you break on the entry to the routine and look in CPU view you should see that the variables are read from the stack, and determine that each one is set correctly. You should be able to put the address of pBuf into the memory window and determine if it is pointing at JPEG data from the JPEG ssignature.

You can also look at the memory dump of the stack to check what has been passed to you and see if you can identify where the variables you need really are.