Question about converting a function pointer to a delegate

This forum is meant for questions and discussions about the X# language and tools
Kai
Posts: 37
Joined: Fri Dec 15, 2017 10:43 am
Location: D

Question about converting a function pointer to a delegate

Post by Kai »

Hello everybody!

I need your help again.
I'm having trouble converting a function pointer from VO to a delegate in XSharp.

The function worm_export_tar of the WORMAPI.DLL of the german "Kassensicherungsverordnung" needs a functionpointer to export data from the securitymodul (TSE = technische Sicherheitseinrichtung).

WORMAPI.DLL was created by Swissbit who developed the TSE system.
WORMAPI.DLL is not under my control and I assume that the functions it contains will work correctly.

The worm_export_tar function makes multiple calls to a callback function and passes a memory pointer containing the data to be written to the export file.

In VO the following code works fine:

METHOD ExportTar(cDateiname AS STRING) AS INT PASCAL CLASS WormAccess
LOCAL hFile AS PTR
LOCAL nRet AS INT

nRet := -1

IF !(SELF:hWormContext == NULL_PTR)
hFile := FCreate(cDateiname, FC_NORMAL)
IF !(hFile == F_ERROR)

//The pointer to the callback function is passed here

nRet := worm_export_tar(SELF:hWormContext, @CallbackExportWormTar(), hFile)

FClose(hFile)
ELSE
nRet := -2
ENDIF
ENDIF

RETURN nRet

//This is the callback-funkction which writes the data to the export-file:

STATIC FUNCTION CallbackExportWormTar(hDaten AS PTR, nLen AS DWORD, hFile AS PTR) AS INT
LOCAL nRet AS INT
IF FWrite3(hFile, hDaten, nLen) == nLen
nRet := 0
ELSE
nRet := -1
ENDIF
RETURN nRet


STATIC GLOBAL hProc_worm_export_tar AS TF_worm_export_tar PTR

FUNCTION TF_worm_export_tar(hWormContext AS PTR, hCallbackFunktion AS PTR, hFile AS PTR) AS INT PASCAL
RETURN 0

FUNCTION worm_export_tar(hWormContext AS PTR, hCallbackFunktion AS PTR, hFile AS PTR) AS INT PASCAL
RETURN PCALL(hProc_worm_export_tar, hWormContext, hCallbackFunktion, hFile)



In XSharp, the following code crashes exactly and reproducibly the third time the callback function is called. No error box is displayed, the program is simply terminated. The only difference between VO and XSharp is that XSharp uses the delegate to invoke the callback function.


DELEGATE CallbackExportWormTar_delegate(hDaten AS PTR, nLen AS DWORD, hFile AS PTR) AS INT

METHOD ExportTar(cDateiname AS STRING) AS INT PASCAL
LOCAL hFile AS PTR
LOCAL nRet AS INT
STATIC LOCAL oCallbackExportWormTarDelegate AS CallbackExportWormTar_Delegate

nRet := -1

IF !(SELF:hWormContext == NULL_PTR)
hFile := FCreate(cDateiname, FC_NORMAL)
IF !(hFile == F_ERROR)

oCallbackExportWormTarDelegate := CallbackExportWormTar

nRet := worm_export_tar(SELF:hWormContext, System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(oCallbackExportWormTarDelegate), hFile)

FClose(hFile)

ELSE
nRet := -2
ENDIF
ENDIF

RETURN nRet

STATIC FUNCTION CallbackExportWormTar(hDaten AS PTR, nLen AS DWORD, hFile AS PTR) AS INT
LOCAL nRet AS INT
IF FWrite3(hFile, hDaten, nLen) == nLen
nRet := 0
ELSE
nRet := -1
ENDIF
RETURN nRet


STATIC GLOBAL hProc_worm_export_tar AS TF_worm_export_tar PTR

FUNCTION TF_worm_export_tar(hWormContext AS PTR, hCallbackFunktion AS PTR, hFile AS PTR) AS INT PASCAL
RETURN 0

FUNCTION worm_export_tar(hWormContext AS PTR, hCallbackFunktion AS PTR, hFile AS PTR) AS INT PASCAL
RETURN PCALL(hProc_worm_export_tar, hWormContext, hCallbackFunktion, hFile)


In both cases (VO and XSharp) the hProc_worm_export_tar variable is initialized once when the TSE system is started:

hLibWorm := LoadLibrary(String2Psz("wormapi.dll"))
hProc_worm_export_tar := GetProcAddress(hLibWorm, String2Psz("worm_export_tar"))


So the question is: What is wrong with the delegate or the callback-function in XSharp what works fine in VO? What's weird is that it works twice and crashes on the third call in XSharp.

Regards
Kai
User avatar
Chris
Posts: 4922
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Question about converting a function pointer to a delegate

Post by Chris »

Hi Kai,

Try running the app in debug mode (Debug->Start integrated debugging). you should get some runtime error message when the crash occurs.
I assume you have set your app to be x86 mode? Also try changing the PTR in the delegate to IntPtr, should not make a difference, but just in case.
Finally, can you produce a sample?

.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
wriedmann
Posts: 3757
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Question about converting a function pointer to a delegate

Post by wriedmann »

Hi Kai,
I have created a X# COM library for this API for someone else and have used the C# WormCApi.dll for the interface to this DLL,
Maybe it could be an idea to do the same and not do all work again.
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
leon-ts
Posts: 435
Joined: Fri Feb 03, 2017 1:43 pm

Question about converting a function pointer to a delegate

Post by leon-ts »

Hi Kai,
Try removing the STATIC modifier:
STATIC FUNCTION CallbackExportWormTar
As far as I understand, in X# it is equivalent to INTERNAL. This may block its call even though it is essentially converted to a direct pointer.

Best regards,
Leonid
Best regards,
Leonid
Kai
Posts: 37
Joined: Fri Dec 15, 2017 10:43 am
Location: D

Question about converting a function pointer to a delegate

Post by Kai »

Hi!

Removing static modifier didn't work.
Unfortunately, changing PTR to intPtr didn't work either.

The debugger reports:

System.Reflection.TargetInvocationException
Ein Aufrufziel hat einen Ausnahmefehler verursacht.

Callstack :
CCTouchpad.Boolean CCTouchpad.Klick(System.Int32 x, System.Int32 y)() : C:XSharpKassePPKasseSharePPKasseShareCCTouchpad.prg : 1169
CCTouchpad.__Usual CCTouchpad.Dispatch(XSharp.__Usual[] Xs$Args)() : C:XSharpKassePPKasseSharePPKasseShareCCTouchpad.prg : 699
XAPP.__Usual XAPP.Start(XSharp.__Usual[] Xs$Args)() : C:XSharpKassePPKasseGastroStart.prg : 386
static Int32 PPKasseGastro.Exe.Functions.Start()() : C:XSharpKassePPKasseGastroStart.prg : 13

None of the lines in the callstack seem to have anything to do with the callback function or the export

Chris, I can produce a sample but you can't run it without a TSE.
User avatar
Chris
Posts: 4922
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Question about converting a function pointer to a delegate

Post by Chris »

Hi Kai,

Does your Start() function have a [STAThread] attribute? If not, please add it and see it it makes a difference.

Also what is the code around line 1169 in CCTouchpad.prg ?
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
Kai
Posts: 37
Joined: Fri Dec 15, 2017 10:43 am
Location: D

Question about converting a function pointer to a delegate

Post by Kai »

Hi Chris!

To make sure that the error has nothing to do with my custom control buttons (CCTouchpad), I created a simple application with just one standard button from the toolbox which starts te export from the TSE. When I click the button, the debugger reports:

System.Reflection.TargetInvocationException
Ein Aufrufziel hat einen Ausnahmefehler verursacht.

Callstack :
DialogHauptfenster.__Usual DialogHauptfenster.Dispatch(XSharp.__Usual[] Xs$Args)() : C:XSharpKasseApplicationsTestPrgDialogHauptfenster.prg : 12
XApp.__Usual XApp.Start(XSharp.__Usual[] Xs$Args)() : C:XSharpKasseApplicationsTestPrgStart.prg : 17
static Int32 Test.Exe.Functions.Start()() : C:XSharpKasseApplicationsTestPrgStart.prg : 6

The start-function has a [STAThread]; attribute

[STAThread];
FUNCTION Start() AS INT
LOCAL oXApp AS XApp
TRY
oXApp := XApp{}
oXApp:Start()
CATCH oException AS Exception
ErrorDialog(oException)
END TRY
RETURN 0

CLASS XApp INHERIT App
METHOD Start()
LOCAL oWin AS DialogHauptfenster

oWin := DialogHauptfenster{SELF}
oWin:Show()

SELF:Quit()

RETURN SELF


END CLASS
comitas2
Posts: 48
Joined: Thu Jul 18, 2019 9:13 am
Location: Germany

Question about converting a function pointer to a delegate

Post by comitas2 »

Hi Kai,
I created the following in Vulcan and ported it to X# some time ago as a test and there were no errors. Maybe there is something that will help you.

Code: Select all

[DllImport("WormAPI.dll", CallingConvention:=CallingConvention.Cdecl)] FUNCTION worm_export_tar(context AS IntPtr, callback AS WormExportTarCallback, callbackData AS IntPtr) AS INT PASCAL

METHOD exportTar(callback AS WormExportTarCallback) AS VOID
LOCAL result AS INT

result := worm_export_tar(_MEMVARVO():worm_context, callback, IntPtr.Zero)
IF (result != 0)
    THROW System.Exception{"Fehler beim Export: " + AllTrim(STR(result))} //springt in die nächst darübergelegene Catch-Fehler-Anzeige + RETURN
ENDIF

RETURN

EXPORT worm_context AS IntPTR      //TSE Variable in übergeordneterStartklasse definiert


METHOD btnExportTAR( )                      //bei click auf Export-Button
LOCAL saveFile AS SaveFileDialog
LOCAL cfileOut AS STRING
LOCAL MyExport AS WormExportTarCallback

IF !btnZugriff()
    RETURN NIL
ENDIF

TRY
    saveFile := SaveFileDialog{}
    saveFile:FileName := "tse.tar"
    saveFile:Filter := "TAR archive (*.tar)|*.tar|All files (*.*)|*.*"
    IF (saveFile:ShowDialog() != DialogResult.OK)
        RETURN
    ENDIF

    cfileOut     := saveFile:FileName
    stream2      := System.IO.FileStream{cfileOut,System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write}
    exportSize:=exportSize0:= myWorm:Info():tarExportSize()
    exportedSize := 0

	oDCProgressBar1:Range := Range{1, 100}    //exportProgress.Value := 0
	oDCProgressBar1:UnitSize := 10	// Setting the Unitsize
	oDCProgressBar1:Position := 1 	// Setting the thumb to the start of the progress bar
	oDCProgressBar1:show()    //exportProgress.Visible := TRUE

    MyExport := WormExportTarCallback{SELF,@WormExportTarCallback()}  //bei größeren Datenmengen wird ggf. WormExportTarCallback()von der LIB mehrfach gerufen
    myWorm:exportTar(MyExport)

//    stream2:Close()
            
CATCH e AS Exception
    ErrorBox{ NIL, e:Message }:Show()
    oDCtxtResult:TextValue :=  e:Message
    RETURN            
FINALLY
    MessageBox.Show("Es wurden "+Str(exportedSize)+" Bytes exportiert.", "Export-Ergebnis:" , MessageBoxButtons.OK, MessageBoxIcon.Information)
	oDCProgressBar1:Hide()  //    exportProgress.Visible = FALSE
END TRY

RETURN NIL

METHOD WormExportTarCallback(chunk AS IntPtr, chunkLength AS DWORD, callbackData AS IntPtr ) AS INT
//hier sollten die Daten aus dem Callback landen...
LOCAL bytes AS BYTE[]
LOCAL Len AS INT

Len := (INT)chunkLength                                  //int Len = (int)chunkLength;
IF (exportSize < chunkLength)                            // IF (mTSEExportSize < chunkLength)
    Len := (INT)exportSize                               // Len = (INT)mTSEExportSize;
ENDIF

bytes := BYTE[] {Len}                                    // BYTE[] ARRAY = new BYTE[Len]; //chunkLength
Marshal.Copy(chunk, bytes, 0, Len)                       // Marshal.Copy(chunk, ARRAY, 0, Len); //(INT)chunkLength
stream2:Write(bytes, 0, Len)                             // mTSETarExport.Write(ARRAY, 0, Len); //System.Buffer.ByteLength(bytes)
exportedSize += (UInt64)Len                              //chunkLength
exportSize   -= (UInt64)Len                              //mTSEExportSize -= (UInt64)Len //chunkLength
bytes := NULL_OBJECT

IF (exportSize == 0)                                     // IF (mTSEExportSize == 0)
    stream2:Close()                                      // mTSETarExport.Close();
ENDIF

oDCProgressBar1:Position := (INT) (100 * exportedSize / exportSize0)  //exportProgress.Value = (int) (100 * exportedSize / exportSize);
ApplicationExec( ExecWhileEvent )  //Zeit zum Balken zeichnen 	

RETURN 0
User avatar
Chris
Posts: 4922
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Question about converting a function pointer to a delegate

Post by Chris »

Hi Kai,

What's the code around line 12 of DialogHauptfenster.prg ?
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
Kai
Posts: 37
Joined: Fri Dec 15, 2017 10:43 am
Location: D

Question about converting a function pointer to a delegate

Post by Kai »

Chris, line 12 invokes the method ExportTar() from the code in my first post.

Here is the full code of DialogHauptfenster:

CLASS DialogHauptfenster INHERIT DialogHauptfensterAbstrakt
CONSTRUCTOR(oOwner)
SUPER(oOwner)
RETURN
METHOD ButtonTest AS VOID PASCAL
LOCAL oWormAccess AS WormAccess
LOCAL oTB AS TextBox

oWormAccess := WormAccess{"T"}

IF oWormAccess:nWormError == WORM_ERROR_NOERROR
oWormAccess:ExportTar( "c:testexport.tar") //Cris: Line 12
oWormAccess:Destroy()

oTB := TextBox{, "test", "OK"}
oTB:Show()
ELSE
oTB := TextBox{, "test", "fehler"}
oTB:Show()
ENDIF
METHOD Dispatch(oEvent)
IF oEvent:Message == WM_COMMAND
IF oEvent:wParam == IDCANCEL
SELF:Enddialog()
ENDIF
ENDIF
RETURN SUPER:Dispatch(oEvent)

END CLASS



If I remove the Dispatch-Method from the code the debugger reports:

System.Reflection.TargetInvocationException
Ein Aufrufziel hat einen Ausnahmefehler verursacht.

Callstack :
XApp.__Usual XApp.Start(XSharp.__Usual[] Xs$Args)() : C:XSharpKasseApplicationsTestPrgStart.prg : 17
static Int32 Test.Exe.Functions.Start()() : C:XSharpKasseApplicationsTestPrgStart.prg : 6
Post Reply