Seite 1 von 1

Amibroker AFL - Plugin

Verfasst: 07.08.2010 00:44
von VoSs2o0o
Hallo,

hab mich gerade mit der Plugin-Schnittstelle vom Amibroker (Börsensoftware von http://www.amibroker.com) beschäftigt.
Ich will ein AFL-Plugin schreiben, und zumindest alles was ich brauche funktioniert erstmal.
Leider habe ich aber nicht das "SiteInterface" zum laufen bekommen welches so in C/C++ definiert ist:

Code: Alles auswählen

struct SiteInterface 
{
				int			nStructSize;
				int			(*GetArraySize) (void);	   
				float *		(*GetStockArray)( int nType );
				AmiVar		(*GetVariable) ( const char *pszName );
				void		(*SetVariable) ( const char *pszName, AmiVar newValue );
				AmiVar		(*CallFunction) ( const char *szName, int nNumArgs, AmiVar *ArgsTable );
				AmiVar		(*AllocArrayResult) (void);
				void *		(*Alloc) (unsigned int nSize);
				void		(*Free) (void *pMemory);
				DATE_TIME_INT* (*GetDateTimeArray) (void);	  // new in 5.30
};
ich habe das draus gemacht:

Code: Alles auswählen

Structure SiteInterface
  nStructSize.l
  *GetArraySize.pGetArraySize
  *GetStockArray.pGetStockArray
  *GetVariable.pGetVariable
  *SetVariable.pSetVariable
  *mCallFunction.pCallFunction
  *AllocArrayResult.pAllocArrayResult
  *Alloc.pAlloc
  *Free.pFree
  *GetDateTimeArray.pGetDateTimeArray
EndStructure
es kommt aber zu Speicherfehlern und ich bekomme auch kein Pointer zurück wenn ich versuche auf eine
Funktion zuzugreifen. z.B. selbst ein "einfaches" GetVariable funktioniert einfach nicht, und wird mit Speicherfehlern vom Amibroker quittiert.

Hier mal ein Code eines AFL-Plugins, welches für Amibroker die Funktion "BTS_Test" zur Verfügung stellt.
Hier ist sicherlich "überflüssiges" aus Vergangenen Tests drin, und man könnte es sicherlich ordentlicher schreiben,
aber grundlegend funktionierts erstmal - bis leider auf das "SiteInterface"

Code: Alles auswählen

#PLUGIN_VERSION = 10000
#PLUGIN_NAME = "Test"
#VENDOR_NAME = "VoSs2o0o"

#NumberofFunctions = 1

Enumeration
  #PLUGIN_TYPE_AFL = 1
  #PLUGIN_TYPE_DATA
  #PLUGIN_TYPE_AFL_AND_DATA
  #PLUGIN_TYPE_OPTIMIZER
EndEnumeration

Enumeration 
  #VAR_NONE
  #VAR_FLOAT
  #VAR_ARRAY
  #VAR_STRING
  #VAR_DIS
EndEnumeration

Structure AmiVar
  type.l
  StructureUnion
    val.f
    *mArray.f[0]
    *string.STRING
    *disp.l
  EndStructureUnion
EndStructure

Structure PluginInfo
  nStructSize.l     ;// this is SizeOf( struct PluginInfo )
  nType.l           ; // plug-in type currently 1 - indicator is the only one supported
  nVersion.l        ; // plug-in version coded to int as MAJOR*10000 + MINOR * 100 + RELEASE
  nIDCode.l         ;// ID code used to uniquely identify the data feed (set it to zero for AFL plugins)
  szNname.s{64}     ;// long name of plug-in displayed in the Plugin dialog
  szVendor.s{64}    ;;// name of the plug-in vendor
  nCertificate.l    ;// certificate code - set it to zero for private plug-ins
  nMinAmiVersion.l ;// minimum required AmiBroker version (should be >= 380000 -> AmiBroker 3.8)
EndStructure

Prototype.l pGetArraySize()
Prototype.f pGetStockArray(nType.l)
Prototype.l pGetVariable(*pszName.STRING)
Prototype.l pSetVariable(*pszName.s, *newValue.AmiVar)
Prototype.l pCallFunction(szName.s, nNumArgs.l, *ArgsTable.AmiVar)
Prototype.l pAllocArrayResult()
Prototype.l pAlloc(nSize.l)
Prototype.l pFree(*pMemory)
Prototype.q pGetDateTimeArray()

Structure SiteInterface
  nStructSize.l
  *GetArraySize.pGetArraySize
  *GetStockArray.pGetStockArray
  *GetVariable.pGetVariable
  *SetVariable.pSetVariable
  *mCallFunction.pCallFunction
  *AllocArrayResult.pAllocArrayResult
  *Alloc.pAlloc
  *Free.pFree
  *GetDateTimeArray.pGetDateTimeArray
EndStructure

Structure FARRAY
  f.f[0]
EndStructure

Structure AMIARRAY
  ami.AmiVar[0]
EndStructure

Prototype.l pFunction(NumArgs.l, Array *ArgsTable.Amivar(1))
Structure FunDesc
  Function.l
  ArrayQty.b
  StringQty.b
  FloatQty.b
  DefaultQty.b
  *DefaultValues.FARRAY
EndStructure

Structure FunctionTag
  Name.STRING
  Descript.FunDesc
EndStructure

ProcedureDLL AttachProcess(Instanz.l)
  Global Dim FunctionTable.FunctionTag(#NumberofFunctions )
  Global Dim *myArgs.Amivar(0)
  Global *gpinterface.SiteInterface
  Global *erg.AmiVar
  *erg.AmiVar = AllocateMemory(1000)
  
  Global strParam1.STRING
EndProcedure

ProcedureCDLL.l GetPluginInfo(*pinfo.Plugininfo)

  *pinfo\nStructSize = SizeOf(PluginInfo)
  *pinfo\nType = #PLUGIN_TYPE_AFL
  *pinfo\nVersion = #PLUGIN_VERSION
  *pinfo\nIDCode = 0
  *pinfo\szNname = #PLUGIN_NAME
  *pinfo\szVendor = #VENDOR_NAME
  *pinfo\nCertificate = 0
  *pinfo\nMinAmiVersion = 530000
  ProcedureReturn 1
EndProcedure

ProcedureCDLL.l Init()

  ProcedureReturn 1
EndProcedure

ProcedureCDLL.l Release()

  ProcedureReturn 1
EndProcedure

ProcedureCDLL.l SetSiteInterface(*pinterface.SiteInterface)
  *gpinterface = *pinterface
  ProcedureReturn 1
EndProcedure


ProcedureC.l BTS_Test(NumArgs.l, *pArgsTable.AmiVar)
   
  MessageRequester("NumArgs", Str(NumArgs.l))
  If NumArgs.l > 0
    ReDim *myArgs.Amivar(NumArgs.l - 1)
    For i=0 To NumArgs.l - 1
      *myArgs(i) = *pArgsTable + (i*SizeOf(AmiVar))
      
      MessageRequester("CurrentArg", Str(i))
      MessageRequester("type", Str(*myArgs(i)\type))
      MessageRequester("val", Str(*myArgs(i)\val))
    Next
  EndIf 

;######################################

  
  ProcedureReturn 1
EndProcedure



ProcedureCDLL.l GetFunctionTable(*ppFunctionTable)

  FunctionTable(0)\Name\s = "BTS_Test"
  FunctionTable(0)\Descript\Function = @BTS_Test()
  FunctionTable(0)\Descript\ArrayQty = 0
  FunctionTable(0)\Descript\StringQty = 0
  FunctionTable(0)\Descript\FloatQty = 0
  FunctionTable(0)\Descript\DefaultQty = 3
  
  *FunctionTable_DefaultValues.FARRAY = AllocateMemory(FunctionTable(0)\Descript\DefaultQty)
  *FunctionTable_DefaultValues\f[0] = 1
  *FunctionTable_DefaultValues\f[1] = 2
  *FunctionTable_DefaultValues\f[2] = 5
  
  PokeL(@FunctionTable(0)\Descript\DefaultValues, *FunctionTable_DefaultValues)
  PokeL(*ppFunctionTable, @FunctionTable(0))
  
  ProcedureReturn #NumberofFunctions 
EndProcedure

Ich hoffe das jemand weiterhelfen kann bezüglich des "siteinterfaces"
PS: Das Amibroker "ADK" gibts auf der Downloadseite von Amibroker.com, und enthält eine Anleitung und
C/C++ Beispielcodes wie ein solch ein Plugin zu schreiben ist.

Re: Amibroker AFL - Plugin

Verfasst: 07.08.2010 01:09
von Kiffi
auf Crosspostings solltest man der Fairness halber hinweisen:

http://www.purebasic.fr/english/viewtop ... 13&t=43168

Re: Amibroker AFL - Plugin

Verfasst: 07.08.2010 13:26
von mk-soft
Du must ein Interface anlegen und nicht eine Struktur. In einer Funktion in einen Interface ist immer der erste Parameter ein Pointer auf das eigene Objekt.
Must Dich also erst mal mit objektorientierte Programmierung beschäftigen.

Such erst mal im Forum wie das mit Interfaces funktioniert.

Ein ansatz ist schon mal hier.
http://www.purebasic.fr/german/viewtopi ... ol#p277062

FF :!:

Re: Amibroker AFL - Plugin

Verfasst: 07.08.2010 13:55
von edel
Nur weil das Ding Interface heisst, muss es noch lange nix mit den PB Interface zu tun haben.

In dem c++ Beispiel ist alles auf cdecl gestellt, versuch daher mal mit PrototypeC.

Re: Amibroker AFL - Plugin

Verfasst: 07.08.2010 22:54
von VoSs2o0o
Richtig, es wird durch Structure und Prototype ein Interface emuliert,
und heute Morgen habe ich genau das gesehen was edel schreibt:

Richtig da gehört PrototypeC hin, da alles "cdecl" ist (immer diese "Schusslichkeitsfehler...." )

Da aber zusätzlich hier kein Pointer als Ergebnis zurückgegeben wird, sondern direkt die Struktur
(was in Purebasic leider nicht möglich ist),
werd ich erst morgen oder Montag einen vollständig funktionierenden Code posten.

Werde mal mit Poke testen, und zur Not ein Quad "missbrauchen" da es ja nur 8 Byte sind....

Trotzdem, danke für die schnelle Hilfe.
Bei dieser ganzen C-Pointer Wurschtelei sieht man irgendwann den Wald vor lauter Bäumen nicht mehr...

PS: weiter unten war auch noch ein Fehler bei der Speicher-Allocation...

Re: Amibroker AFL - Plugin

Verfasst: 08.08.2010 09:27
von edel
Versuch es mal so :

Code: Alles auswählen

ProcedureCDLL.l SetSiteInterface(*pinterface.Integer)
  *gpinterface = *pinterface\i
  ProcedureReturn 1
EndProcedure

Re: Amibroker AFL - Plugin

Verfasst: 08.08.2010 14:20
von VoSs2o0o
Danke edel, aber es geht um die Rückgabe-Ergebnisse der Funktionen des "SiteInterfaces".
Die Funktionen liefern teilweise eine "AMIVAR" Struktur zurück.
Leider mag Purebasic keine Strukturen als Rückgabeparameter. Ich habe nun ein ".q" zur Hilfe genommen,
da die AMIVAR Struktur glücklicherweise "nur" 8 Bytes benötigt.
(Da wo das "mCallFunction" bzw das "Getvariable" aufgerufen wird).

Vielleicht gibts ja hier noch ein Trick wie man das "besser" lösen kann - wahrscheinlich ist das aber normal so, da der Amibroker-Programmierer ebenfalls C/C++ an dieser Stelle ein wenig "überstrapaziert" hat......

Hier also ein vollständig funktionierender Code:

Code: Alles auswählen

#PLUGIN_VERSION = 10000
#PLUGIN_NAME = "Test"
#VENDOR_NAME = "VoSs2o0o"

#NumberofFunctions = 1

Enumeration
  #PLUGIN_TYPE_AFL = 1
  #PLUGIN_TYPE_DATA
  #PLUGIN_TYPE_AFL_AND_DATA
  #PLUGIN_TYPE_OPTIMIZER
EndEnumeration

Enumeration 
  #VAR_NONE
  #VAR_FLOAT
  #VAR_ARRAY
  #VAR_STRING
  #VAR_DIS
EndEnumeration

Structure AmiVar
  type.l
  StructureUnion
    val.f
    *mArray.FARRAY[0]
    *string.STRING
    *disp.l
  EndStructureUnion
EndStructure

Structure PluginInfo
  nStructSize.l     ;// this is SizeOf( struct PluginInfo )
  nType.l           ; // plug-in type currently 1 - indicator is the only one supported
  nVersion.l        ; // plug-in version coded to int as MAJOR*10000 + MINOR * 100 + RELEASE
  nIDCode.l         ;// ID code used to uniquely identify the data feed (set it to zero for AFL plugins)
  szNname.s{64}     ;// long name of plug-in displayed in the Plugin dialog
  szVendor.s{64}    ;;// name of the plug-in vendor
  nCertificate.l    ;// certificate code - set it to zero for private plug-ins
  nMinAmiVersion.l ;// minimum required AmiBroker version (should be >= 380000 -> AmiBroker 3.8)
EndStructure

PrototypeC.l pGetArraySize()
PrototypeC.l pGetStockArray(nType.l)
PrototypeC.q pGetVariable(*pszName.s)
PrototypeC.l pSetVariable(*pszName.s, *newValue.AmiVar)
PrototypeC.q pCallFunction(szName.s, nNumArgs.l, *ArgsTable.AmiVar)
PrototypeC.q pAllocArrayResult()
PrototypeC.l pAlloc(nSize.l)
PrototypeC.l pFree(*pMemory)
PrototypeC.q pGetDateTimeArray()

Structure SiteInterface
  nStructSize.l
  *GetArraySize.pGetArraySize
  *GetStockArray.pGetStockArray
  *GetVariable.pGetVariable
  *SetVariable.pSetVariable
  *mCallFunction.pCallFunction
  *AllocArrayResult.pAllocArrayResult
  *Alloc.pAlloc
  *Free.pFree
  *GetDateTimeArray.pGetDateTimeArray
EndStructure

Structure FARRAY
  f.f[0]
EndStructure

Structure AMIARRAY
  ami.AmiVar[0]
EndStructure

Prototype.l pFunction(NumArgs.l, Array *ArgsTable.Amivar(1))
Structure FunDesc
  Function.l
  ArrayQty.b
  StringQty.b
  FloatQty.b
  DefaultQty.b
  *DefaultValues.FARRAY
EndStructure

Structure FunctionTag
  Name.STRING
  Descript.FunDesc
EndStructure

ProcedureDLL AttachProcess(Instanz.l)
  Global Dim FunctionTable.FunctionTag(#NumberofFunctions )
  Global Dim *myArgs.Amivar(0)
  Global *gpinterface.SiteInterface
  
  Global *erg.AmiVar
  Global Dim myInput.Amivar(0)
EndProcedure

ProcedureCDLL.l GetPluginInfo(*pinfo.Plugininfo)

  *pinfo\nStructSize = SizeOf(PluginInfo)
  *pinfo\nType = #PLUGIN_TYPE_AFL
  *pinfo\nVersion = #PLUGIN_VERSION
  *pinfo\nIDCode = 0
  *pinfo\szNname = #PLUGIN_NAME
  *pinfo\szVendor = #VENDOR_NAME
  *pinfo\nCertificate = 0
  *pinfo\nMinAmiVersion = 530000
  ProcedureReturn 1
EndProcedure

ProcedureCDLL.l Init()

  ProcedureReturn 1
EndProcedure

ProcedureCDLL.l Release()

  ProcedureReturn 1
EndProcedure

ProcedureCDLL.l SetSiteInterface(*pinterface.SiteInterface)
  *gpinterface = *pinterface
  ProcedureReturn 1
EndProcedure


ProcedureC.l BTS_Test(NumArgs.l, *pArgsTable.AmiVar)
   
  MessageRequester("NumArgs", Str(NumArgs.l))
  If NumArgs.l > 0
    ReDim *myArgs.Amivar(NumArgs.l - 1)
    For i=0 To NumArgs.l - 1
      *myArgs(i) = *pArgsTable + (i*SizeOf(AmiVar))
      
      MessageRequester("CurrentArg", Str(i))
      MessageRequester("type", Str(*myArgs(i)\type))
      MessageRequester("val", Str(*myArgs(i)\val))
    Next
  EndIf 

;######################################

   MessageRequester("Test Amibroker Variables", "Read First Close Array values:")  
   strParam2.s = "close"
   *erg.Amivar = 0
   q_erg.q = 0
   q_erg.q = *gpinterface\GetVariable(@strParam2.s)
   *erg = @q_erg.q
   If *erg\type = #VAR_ARRAY
     For i=0 To 2
       MessageRequester("GetVariable close[" + Str(i) + "]", StrF(*erg\mArray\f[i]))
     Next
   EndIf


;#####################################
  MessageRequester("Test Amibroker Commands", "Try a version()")

  myinput.Amivar(0)\type = #VAR_NONE
  myinput.Amivar(0)\val = 0
  
  *erg.Amivar = 0
  q_erg.q = 0
  q_erg.q = *gpinterface\mCallFunction("version", 1, @myinput(0))
  *erg = @q_erg.q
  
  MessageRequester("Erg of version:", StrF(*erg\val))
  
  ProcedureReturn 1
EndProcedure



ProcedureCDLL.l GetFunctionTable(*ppFunctionTable)

  FunctionTable(0)\Name\s = "BTS_Test"
  FunctionTable(0)\Descript\Function = @BTS_Test()
  FunctionTable(0)\Descript\ArrayQty = 0
  FunctionTable(0)\Descript\StringQty = 0
  FunctionTable(0)\Descript\FloatQty = 0
  FunctionTable(0)\Descript\DefaultQty = 3
  
  *FunctionTable_DefaultValues.FARRAY = AllocateMemory(FunctionTable(0)\Descript\DefaultQty * 4)
  *FunctionTable_DefaultValues\f[0] = 1
  *FunctionTable_DefaultValues\f[1] = 2
  *FunctionTable_DefaultValues\f[2] = 5
  
  PokeL(@FunctionTable(0)\Descript\DefaultValues, *FunctionTable_DefaultValues)
  PokeL(*ppFunctionTable, @FunctionTable(0))
  
  ProcedureReturn #NumberofFunctions 
EndProcedure

PS: Ich bin immer wieder beeindruckt wie schnell einem hier geholfen wird - dieses Forum ist wirklich das beste :-)