It seems the "CallFuncPtr" function is basically setting up memory with the correct addresses and parameters so a call can be made to the function which is implemented inside the DLL plugin. Is this correct? If so, then I guess this function can simply be dropped into a VB project and used as is?
1) Create a directory where all plugin files (DLL files) will be placed (ex: new "plugin" directory)
2) I will need to define routine names (ex: UTJuke_SongTrackStart(), UTJuke_SongTrackEnd(), UTJuke_SongTrackSkipped(), etc, etc) along with associated parameter lists for each routine so the plugin developers know which routines will be called by my application in case they are interested in implementing these events in their plugin. Basically, all the routine names/parameters need to be defined for use in all plugins.
3) When a certain event occurs in my jukebox software, then I should loop through all of the plugin files (located in the plugins directory) and verify whether the "UTJuke_Check()" routine exists. If so, then the plugin (DLL) file is a "true" plugin file. I should then verify whether the appropriate event plugin API (ex: UTJuke_SongTrackStart(), UTJuke_SongTrackEnd(), UTJuke_SongTrackSkipped(), etc, etc) exists in the plugin file. If so, then call the "CallFuncPtr" function accordingly.
Function IsProcedureAvailable(ByVal ProcedureName As String, _
ByVal DllFilename As String) As Boolean
Dim hModule As Long, procAddr As Long
' first, attempt to load the module
hModule = LoadLibrary(DllFilename)
If hModule Then
' then, retrieve the address of the routine
procAddr = GetProcAddress(hModule, ProcedureName)
' finally, decrement the DLL usage counter
FreeLibrary hModule
End If
' if procAddr is non-zero, the function is available
IsProcedureAvailable = (procAddr <> 0)
End Function
Am I missing anything?
struct SongInfo
{
char[256] Name;
char[256] Artist;
char[256] Album;
};
Type SongInfo
Name as String * 256
Artist as String * 256
Album as String * 256
End Type
The only problem I see with your code is you can only use one dll (one plugin). So not really much of a plugin system.
4. Display a list of plugins you can enabled / disable using a checked listbox. This might require you write a simple "front end" like the "Plugin Manager" I wrote for GameEx.
But I'm are not Visual Basic man, so I guess Barcrest, headkaze, loadman to develop a good plug in system.
library JukePlug;
uses
Dialogs;
{*******************************************************************************}
procedure Juke_GetPluginInfo(); stdcall;
begin
end;
{*******************************************************************************}
procedure Juke_SongStart(); stdcall;
begin
Showmessage('Song has started');
end;
{*******************************************************************************}
procedure Juke_SongEnd(); stdcall;
begin
Showmessage('Song has ended');
end;
{*******************************************************************************}
exports
Juke_GetPluginInfo,
Juke_SongStart,
Juke_SongEnd;
{$R *.res}
begin
end.
I will get onto this later today now we have some test plugins i can inerface with to see if it's working that will be great.
Of course the real work does come from plugin authors once the system is in place. Anyone got ant thoughts on plugin config screens like in winamp? I assume we add a button to our jukebox software that says config and this calls the config part of the plugin?
typedef struct
{
char Name[256];
char Author[256];
char Version[256];
} PluginInfo;
typedef struct
{
char Name[256];
char Artist[256];
char Album[256];
} SongInfo;
// JukePlugin.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include "JukePlugin.h"
// standard windows headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#define PLUGIN_NAME "Test Plugin"
#define PLUGIN_AUTHOR "Ben Baker"
#define PLUGIN_VERSION "1.0"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
JUKEPLUGIN_API int __stdcall Juke_GetPluginInfo(PluginInfo *pluginInfo)
{
strncpy(pluginInfo->Name, PLUGIN_NAME, 256);
strncpy(pluginInfo->Author, PLUGIN_AUTHOR, 256);
strncpy(pluginInfo->Version, PLUGIN_VERSION, 256);
return 1;
}
JUKEPLUGIN_API int __stdcall Juke_SongStart(SongInfo *songInfo)
{
char buf[512];
sprintf(buf, "Song has started, Name: %s, Album: %s, Artist: %s", songInfo->Name,
songInfo->Album, songInfo->Artist);
MessageBox(NULL, buf, "Message", MB_OK);
return 1;
}
JUKEPLUGIN_API int __stdcall Juke_SongEnd(SongInfo *songInfo)
{
char buf[512];
sprintf(buf, "Song has ended, Name: %s, Album: %s, Artist: %s", songInfo->Name,
songInfo->Album, songInfo->Artist);
MessageBox(NULL, buf, "Message", MB_OK);
return 1;
}
Public Type SongInfo
Title As String * 256
artist As String * 256
album As String * 256
End Type
Public Sub do_plugin()
Dim hDll As Long
Dim pTEST As Long
Dim sng As SongInfo
sng.artist = Form2.txtArtist.Text
sng.album = Form2.txtAlbum.Text
sng.Title = Form2.txtTitle.Text
hDll = LoadLibrary(App.path + "\Plugins\JukePlugin.dll")
pTEST = GetProcAddress(hDll, "Juke_SongStart")
CallFuncPtr pTEST, sng
FreeLibrary hDll
End Sub
Public Sub do_plugin()
Dim hDll As Long
Dim pTEST As Long
sng.artist = Form2.txtArtist.Text
sng.album = Form2.txtAlbum.Text
sng.Title = Form2.txtTitle.Text
hDll = LoadLibrary(App.path + "\Plugins\JukePlugin.dll")
pTEST = GetProcAddress(hDll, "Juke_SongStart")
CallFuncPtr pTEST, sng.artist & sng.album & sng.Title
FreeLibrary hDll
End Sub
CallFuncPtr pTEST, "TEST"
CallFuncPtr pTEST, sng.artist & sng.album & sng.Title
In Pure Basic you need to use a global result variable to aviod a crash. Might it can been the problem with using strings.
I'm currectly just got Juke_init() to work in the wrapepr and get all plugin names.
Public Type PLUGIN_DESC_TYPE
Name As String * 256
Author As String * 256
Version As String * 256
End Type
Dim pluginDesc As PLUGIN_DESC_TYPE
CallFuncPtr pFunctionPtr, pluginDesc
Only user defined types defined in public modules can be coerced to or from a variant passed to late bound functions.
.... I get the same error Barcrest mentioned:QuoteOnly user defined types defined in public modules can be coerced to or from a variant passed to late bound functions.
Dim JukePluginArray(10) As JukePlugin
hence that is why I now creating a SDK with a wrapper that dosent use any type of arrays and do all the work using a simple linked list (do arrays).
Does VB6 actuelly support 16 bit strings (unicode), or do they need to been UTF8 strings?
Struct is not needed in that method I trying to use (since they hard in BlitzMax as well that used a diffecent struct method that is not C++ combatible).
BTW can VB6 send a string pointer to a dll?
I wipe out a wrapper tomorrow to avoid software developers not need to create a plugins list and so on.
loadman
Please change the original post title to be something like "Standardize plugins for Jukebox apps" ...... since it is no longer specific to my Jukebox software ....
Loadman your plugin was detected as a backdoor Trojan and macaffee deleted it?Hmm Well obviously it isn't.
So with these limitations in mind I have come up with some new example code to work from. It has an example VB6 application and C++ dll plugin example.
Other things:
For security reasons (here it nothing by jukebox software it self, but mere about PluginSDK.dll and its plugins), should I rename the dll to something others (Like MALA) and might be due something checking with a md5 file or such? But here I would make sure it actuelly would work before I countinue working on this one.
I kind of like the idea of having everyone place their plugins in my "plugins" directory and having my software determine what plugins are valid, having the user being able to enable/disable them and just having complete control on how this is going to work within my software. Basically, not to crazy about using someone else's wrapper SDK which I have no control over and probably can not update as I see fit in the future .....
Am I misunderstanding something?
Perhaps it will not matter anyway since I am already thinking I am spending way too much time on this enhancement. I might once again prefer to wait until everyone else has this working 100% before I try to understand what is required and then try to decide whether I should spend my time on this or not.
Option Explicit
Private pDC As Long
Private hDll As Long
Private pGetPluginInfo As Long
Private pSongStart As Long
Private pSongEnd As Long
Private PluginInfo As PluginInfoType
Private SongInfo As SongInfoType
Public Sub Initialize(hDC As Long, PluginName As String)
pDC = hDC
hDll = LoadLibrary(PluginName)
pGetPluginInfo = GetProcAddress(hDll, "Juke_GetPluginInfo")
If IsPlugin Then
pSongStart = GetProcAddress(hDll, "Juke_SongStart")
pSongEnd = GetProcAddress(hDll, "Juke_SongEnd")
GetPluginInfo
Else
Shutdown
End If
End Sub
Public Sub Shutdown()
FreeLibrary hDll
End Sub
Public Sub GetPluginInfo()
If pGetPluginInfo <> 0 Then
CallFuncPtr pDC, pGetPluginInfo, PluginInfo.Name, PluginInfo.Author, PluginInfo.Version
End If
End Sub
Public Sub SongStart()
If pSongStart <> 0 Then
SongInfo.Name = "Name" + Chr(0)
SongInfo.Artist = "Artist" + Chr(0)
SongInfo.Album = "Album" + Chr(0)
CallFuncPtr pDC, pSongStart, SongInfo.Name, SongInfo.Artist, SongInfo.Album
End If
End Sub
Public Sub SongEnd()
If pSongEnd <> 0 Then
SongInfo.Name = "Name" + Chr(0)
SongInfo.Artist = "Artist" + Chr(0)
SongInfo.Album = "Album" + Chr(0)
CallFuncPtr pDC, pSongEnd, SongInfo.Name, SongInfo.Artist, SongInfo.Album
End If
End Sub
Public Property Get IsPlugin() As Boolean
IsPlugin = (pGetPluginInfo <> 0)
End Property
Public Property Get Name() As String
Name = PluginInfo.Name
End Property
Public Property Get Author() As String
Author = PluginInfo.Author
End Property
Public Property Get Version() As String
Version = PluginInfo.Version
End Property
Dim plugin As New JukePlugin
plugin.Initialize Me.hDC, "JukePlugin.dll"
plugin.SongStart
plugin.SongEnd
plugin.Shutdown
I found some time and added headkaze's logic to my software and have some problems:
1) Inside the JukePlugIn class there is a routine called "GetPluginInfo()" which calls "CallFuncPtr()". I just confirmed the "CallFuncPtr()" routine does not populate the name, author or version information at all. Whatever value these text strings have before the call to "CallFuncPtr()", they have after the call to "CallFuncPtr()", although they are always padded with trailing "space" characters until the length of the text string is 256.
Are the parameters being passed by reference (like they should be) or are they perhaps being passed by value instead (which would be incorrect)?
2) Why is the PluginInfoType and SongInfoType structures defined with a "* 256" value next to each field?
3) Why are you using "strncpy" to copy the name, artist and album plugin information? I do not think this appends the null terminator to the end of the string. Am I wrong?
4) I updated the SongStart routine to have three input parameters (ie: name, artist and album). Now, when a song is started in my software, the SongStart routine of the plugin is called and a MessageBox is displayed which shows the song which has been started. When I click on the "OK" box on the MessageBox, then my VisualBasic project closes automatically. No crashes or anything .... just disappears.
Any idea what is going on here?
I can not proceed any further until this questions have been resolved..... :dunno
Have you made the strings global? I have not had chance to check this out so i can't confim if it works or not.
This means it will be a constant 256 characters long, you can use the VB Trim command to remove spaces....
Trim(String)
I don't know anything about this, there doesn't really need to be a terminator as far as i can see...
I reported this issue further back in the thread.
Using CallFuncPtr() has it's limitations and these are
- Can't use structs (aka types in VB6)
- Can't send over Unicode strings
- It only works one way, meaning you can't retrieve data from the plugin like plugin info. But I've left the code in there anyway.
3) Why are you using "strncpy" to copy the name, artist and album plugin information? I do not think this appends the null terminator to the end of the string. Am I wrong?
I would like to see you updated your source and examples to support unicode. Unicode is very important to been supported. Unicode is just a 16 bit wide strings up to 65kb chars (which BlitzMax example use).
Public Sub SongStart(songName As String, songArtist As String, songAlbum As String)
If pSongStart <> 0 Then
SongInfo.Name = songName + Chr(0)
SongInfo.Artist = songArtist + Chr(0)
SongInfo.Album = songAlbum + Chr(0)
CallFuncPtr pDC, pSongStart, SongInfo.Name, SongInfo.Artist, SongInfo.Album
End If
End Sub
Do
' if exit code is 256 the thread is still running
GetExitCodeThread hThread, dwExit
If dwExit <> 259 Then Exit Do
DoEvents
Loop
headkaze[/quote]
1) I updated my code to use your new logic. When the SongStart routine is called with small text strings (ie: name="one", artist="two", album="three") then everything works fine ...... the MessageBox pops up correctly and I can click the OK button to close it and my software continues to run no problem. However, if the text strings passed into the SongStart routine are larger in size, then this causes my Visual Basic project to simply disappear/close/exit (like Barcrest mentioned previously).
Public Type PluginInfoType
Name As String * 256
Author As String * 256
Version As String * 256
End Type
Public Type SongInfoType
Name As String * 256
Artist As String * 256
Album As String * 256
End Type
Public Type PluginInfoType
Name As String * 256
Author As String * 256
Version As String * 256
End Type
Public Type SongInfoType
Name As String
Artist As String
Album As String
End Type
Public Sub SongStart(songName As String, songArtist As String, songAlbum As String)
If pSongStart <> 0 Then
CallFuncPtr pSongStart, songName + Chr(0), songArtist + Chr(0), songAlbum + Chr(0)
End If
End Sub
SongStart and SongEnd routines now work ...... thanks for the updated code.
Do you think a text file with three lines indicating the name, author and version would be worth it to provide VB6.0 applications the plugin information?
+---------------
|Test Plugin
|Ben Baker
|1.0
|
I guess I can always ask for this file and if it does not exist then ignore the plugin information.
Come to think of it ....... the name of the plugin file could simply include a nice name and version and perhaps the author name as well..... that might be easier for everyone.
The new code works fine, thanks Headkaze.
JUKEPLUGIN_API int __stdcall Juke_SongStart(PCHAR Name, PCHAR Artist, PCHAR Album)
{
char buf[512];
sprintf(buf, "Song has started, Name: %s, Album: %s, Artist: %s", Name, Album, Artist);
MessageBox(NULL, buf, "Juke_SongStart", MB_OK);
return 1;
}
Now, I guess you just gotta figure out all the functions you need and make a demo plugin for that. Then Loadman can get onto writing a real plugin :)
If you guys need the JukePlugin.dll example extended with the new functions for testing let me know. I would need a list of all the functions and parameters though.
I think once you guys get plugins going you will be really suprised how much they can enhance your software. All you need is some good plugin coders like loadman who already created a tonne of great ones for Mala :)
library JukePlugin;
uses
Dialogs;
{*******************************************************************************}
procedure Juke_GetPluginInfo(Name: String; Author :STRING; Version:String); stdcall;
begin
Name := 'Load';
Author := 'Me';
Version := 'One';
end;
{*******************************************************************************}
procedure Juke_SongStart(Name: String; Artist :STRING; Album:String); stdcall;
begin
end;
{*******************************************************************************}
procedure Juke_SongEnd(Name: String; Artist :STRING; Album:String); stdcall;
begin
end;
{*******************************************************************************}
exports
Juke_GetPluginInfo,
Juke_SongStart,
Juke_SongEnd;
{$R *.res}
begin
end.
procedure Juke_SongStart(Name: pchar; Artist :STRING; Album:String); stdcall;
begin
Showmessage(Artist);
end;
Loadman: Try PChar no String's just PCharWhat took you so long?? he he ;) Thanks. That works.
Any idea for a sweet name for this Plugin system?
I dropped 16 bit unicode strings for compatible, and instead they now need to been converted to UTF8 before sent to the plug in (if the jukebox software can handle it) and let the plug in convert back.....
function Juke_GetPluginInfo(A: pchar; B :pchar; C:pchar):Pchar ; stdcall;
begin
result := PChar('Test');
end;
Private Sub Command2_Click()
Text1.Text = "Plugin name is: " + plugin.Name
End Sub
Space Fractal (or anyone else)
I have no idea what this means:QuoteI dropped 16 bit unicode strings for compatible, and instead they now need to been converted to UTF8 before sent to the plug in (if the jukebox software can handle it) and let the plug in convert back.....
Does my software need to do anything special for text strings?
I have never wrote a DLL in VB before so i am not sure what needs to be done. I could just tidy up the code and post the source if anyone fancies playing with it....
I have never wrote a DLL in VB before so i am not sure what needs to be done. I could just tidy up the code and post the source if anyone fancies playing with it....
I'm not sure you can? I think VB can only create ActiveX DLL's :( but it can read proper DLL's. :)
Jukebox Standard Plugin System (JSPS) <-- The acronym kind of rolls off your lips
HeadKaze
The following Delphi code is the only one of have found to keep your VB code happy and allow proceeding procedures to workCode: [Select]function Juke_GetPluginInfo(A: pchar; B :pchar; C:pchar):Pchar ; stdcall;
begin
result := PChar('Test');
end;
But how can I do it properly so it send back the details to the Jukebox??
Problems:
1) A function has has one result type
2) VB is not recognising this one result anyway
3) If I don't have the 3 viriables inside the function VB will crash? Weird. It seems like the wrong spot? AHH :banghead:
LATER:
Actually The C++ code example you provided does not appear to pass the data to the VB App either. I added a bit to the VB App:Code: [Select]Private Sub Command2_Click()
Text1.Text = "Plugin name is: " + plugin.Name
End Sub
But No data?? :dizzy:
// QUEUE FUNCTIONS
JUKEPLUGIN_API int __stdcall Juke_AddQueue(PCHAR File, int SongTime, int Auto, PCHAR CoverArt, PCHAR Title, PCHAR Artist, PCHAR Album, PCHAR Genre, PCHAR Special);
JUKEPLUGIN_API int __stdcall Juke_QueueClear(int ReSubmit);
JUKEPLUGIN_API int __stdcall Juke_SongSuggestion(PCHAR File);
// STATUS FUNCTIONS
JUKEPLUGIN_API int __stdcall Juke_SongStart(int Value);
JUKEPLUGIN_API int __stdcall Juke_SongEnd(int Skipped);
JUKEPLUGIN_API int __stdcall Juke_SongPlayed(int Seconds);
JUKEPLUGIN_API int __stdcall Juke_SongStatus(int Status);
// APPLICATION INFO
JUKEPLUGIN_API int __stdcall Juke_GetPluginInfo(PCHAR Name, PCHAR Author, PCHAR Version, int UTF8);
JUKEPLUGIN_API int __stdcall Juke_Volume(int Value);
JUKEPLUGIN_API int __stdcall Juke_Initialize(int Value);
JUKEPLUGIN_API int __stdcall Juke_Shutdown(int Value);
// COMMANDS (Eg Play, Skip, VolumeUp VolumeDown, Lock, UnLock)
JUKEPLUGIN_API int __stdcall Juke_Command(PCHAR Name, PCHAR Value);
// CREDIT FUNCTIONS
JUKEPLUGIN_API int __stdcall Juke_AddCoin(int Coins);
JUKEPLUGIN_API int __stdcall Juke_PaidCoin(int Coins);
For the Juke_Command() routine ..... why are we relying on a command.txt file to be created. Why not create a standard list of set values which can be used in this routine instead.
For example:
JUKE_COMMAND_PLAY
JUKE_COMMAND_VOLUME_UP
JUKE_COMMAND_VOLUME_DOWN
JUKE_COMMAND_PARTY_LOCK_ON
JUKE_COMMAND_PARTY_LOCK_OFF
JUKE_COMMAND_MUTE_ON
JUKE_COMMAND_MUTE_OFF
etc.
etc.
Just a thought .... since I am still not crazy about supporting a "command.txt" file
Mate you can't send data from the plugin that is a limitation of the CallFuncPtr() function I just left the code in there for other languages. I've said it a few times but noone seems to get it. Maybe I should just remove the PluginInfo code?
This is why unclet was thinking of other ways to send the plugin info like using a text file or having the details in the filename.
Here is the plugin with all the functions but I had to add a paramter to the functions without one as the (now famously limited) CallFuncPtr() won't allow it.
It is a video and/or audio music player. The majority of people have MP3 files they want to use on my jukebox and that works perfectly, although some people (including myself) have tons of music videos to watch instead of listening to just MP3 tracks. As a result my jukebox was designed to allow videos as well. My jukebox has nothing to do with Radio stations though ... sorry. I prefer to keep my jukebox software to be more like a real jukebox instead of a radio tuner ;D
They are ASX files, NOT LNK files.
LNK is just a shortcuts to anyway to a net or harddrive. Some nasty webradio does use flash :( today, so they cant been used other than a browser, so LNK might been needed, but just hope they dosent use popups (use a blocker).
Otherwice direct links to net radios is ASX and PLS files and both plays fine in Windows Media Player. Just add these extension to add net radio support.
<ASX version = "3.0" BannerBar = "FIXED">
<Entry>
<Ref href = "mms://media3.abc.net.au/triplej"/>
</Entry>
</ASX>
I think he's talking about having a plugin that adds radio playback to your Jukebox. But if your using the WMP engine for video playback, then it should be able to play streaming radio since WMP can play that. All you need is the ability to run *.lnk files which are shortcuts to the address of the radio station.