Wasn't too sure which board to post this in, so apologies if it's in the wrong place!
If you use VC++ Express edition, you'll know that you can't generate string tables automatically (it's one of the "more advanced" features available in the expensive full version). If your DLL contains just a few exported functions then it won't be a problem, but the plugin I've been developing is weighing in at around 100 commands and still growing.
I've therefore written the following DBP program that scans C++ source code files for functions you're exporting and generates a resource file "StringTable.rc" that you can just include with your C++ project and have DBP immediately recognise all your commands.
Rem Project: String Table Generator
Rem Created: Friday, September 9, 2011
Rem ***** Main Source File *****
// Core variables
DecoratedNamePath$ = "DecoratedNames.txt"
Global Dim DecoratedName$() as String // List of Decorated Names from DLL
Global Dim FileList$() as String
Global g_nStringTableEntries : g_nStringTableEntries = 0
Global g_StringTableFileNum : g_StringTableFileNum = Find Free File(1, 30)
// Program starts
Print "Welcome"
Print "Scanning for .cpp, .c, .cxx files..."
Populate_File_List()
Print "Scanning Complete..."
Output_File_List()
Input "Start String Table at entry:"; Reservation$
g_nStringTableEntries = g_nStringTableEntries + IntVal(Reservation$)
Print "Press any key to continue and start processing..."
wait key
// Open the Stringtable output file
if File Exist("StringTable.rc") then Delete File "StringTable.rc"
Open To Write g_StringTableFileNum , "StringTable.rc"
Write String g_StringTableFileNum , "STRINGTABLE"
Write String g_StringTableFileNum , "{"
Load_Decorated_Names(DecoratedNamePath$)
Print_Decorated_Names()
For FileCounter = 0 to Array Count(FileList$())
Print "Started Processing " + FileList$(FileCounter)
Parse_Source_Code_File( FileList$(FileCounter) )
Print "Completed Processing " + FileList$(FileCounter)
Next FileCounter
Write String g_StringTableFileNum , "}"
Close File g_StringTableFileNum
Print ""
Print "Successfully Processed all C++ code files"
Print "The Stringtable contains " + Str$(g_nStringTableEntries) + " entries"
Print "Please include the file " + Quote$() + "StringTable.rc" + Quote$() + " within your C++ project"
Print "Press any key to exit"
wait key
End
// =============================
// Program Subroutines/Functions
// =============================
// Search for all files in current folder of type .cpp, .c, .cxx
// Log these file names in the FileList$() array
Function Populate_File_List()
Perform Checklist for Files
For T = 1 to Checklist Quantity()
Temp$ = Checklist String$(T)
Temp$ = Fast Lower$(Temp$)
If Fast Right$(Temp$, 4) = ".cpp" or Fast Right$(Temp$, 2) = ".c" or Fast Right$(Temp$, 4) = ".cxx"
Array Insert at Bottom FileList$()
FileList$(Array Count(FileList$())) = Checklist String$(T)
endif
Next T
Empty Checklist
EndFunction
// Print contents of FIleList array to screen
Function Output_File_List()
Print "Program will process the following files:"
For T = 0 to Array Count(FileList$())
Print " " + FileList$(T)
Next T
Print "End of list"
EndFunction
// Read in each line of the input file, split it up around the " " chars
// then store each word as a new entry in the global array DecoratedName$
Function Load_Decorated_Names(Filename$)
FileNum = Find Free File(1, 30)
Open To Read FileNum, Filename$
While (File End(FileNum) = 0)
Read String FileNum, Temp$
Split String Temp$, " "
For T = 1 to Split Count()
Array Insert At Bottom DecoratedName$()
DecoratedName$( Array Count(DecoratedName$())) = Get Split Word$(T)
Next T
EndWhile
Close File FileNum
EndFunction
// Dump contents of DecoratedName$ to screen
Function Print_Decorated_Names()
For T = 0 to Array Count(DecoratedName$())
Print DecoratedName$(T)
Next T
EndFunction
Function Parse_Source_Code_File(Filename$)
Dim Words$() as String
ExportPrefix$ = ""
FileNum = Find Free File(1, 30)
Open To Read FileNum, Filename$
While (File End(FileNum) = 0)
Read String FileNum, Input$
Input$ = Trim$(Input$)
Split String Input$, " "
if ExportPrefix$ = ""
// identify "#define EXPORT __declspec(dllexport)"
if Fast Upper$( Get Split Word$(1) ) = "#DEFINE"
For T = 2 to Split Count()
if Get Split Word$(T) <> ""
Array Insert At Bottom Words$()
Words$( Array Count(Words$()) ) = Get Split Word$(T)
endif
Next T
// if identified, Words$ = (EXPORT, __declspec(, dllexport, ),
Temp$ = ""
For T = 1 to Array Count(Words$())
Temp$ = Temp$ + Words$(T)
Next T
If Lower$(Temp$) = "__declspec(dllexport)"
ExportPrefix$ = Words$(0)
endif
endif
else
// Looking for Exported function statements
// Consider line "EXPORT void DG_Show_Checkbox(int ID)"
if Get Split Word$(1) = ExportPrefix$
Generate_String_Table_Entry(Input$)
endif
endif
// Reset array
Empty Array Words$()
EndWhile
Write String g_StringTableFileNum, " "
Close File FileNum
EndFunction
Function Generate_String_Table_Entry(CPP_Function$)
//Input function is of type "EXPORT void DG_Show_Checkbox(int One = 0, int Test = 5, int Three)"
//Isolate C++ name, generate DBP equivalent
Definition$ = Fast Left$ ( CPP_Function$, Instr(CPP_Function$, "(" ) - 1 )
Split String Definition$, " "
ReturnType$ = Get Split Word$(2)
CPPFuncName$ = Get Split Word$(3)
DBPFuncName$ = Upper$(Replace All$(CPPFuncName$, "_", " "))
// Isolate parameter list
ParamListStart = Instr(CPP_Function$, "(" ) + 1
ParamListEnd = Last Instr(CPP_Function$, ")")
ParameterList$ = Mid$(CPP_Function$, ParamListStart, (ParamListEnd - ParamListStart))
// Handle no input params
if (ParameterList$ = ")") or (ParameterList$ = "")
ParameterList$ = "void"
endif
// Split parameters ("int a", "float b")
Dim Parameter$() as String
Split String ParameterList$, ","
For T = 1 to Split Count()
Array Insert At Bottom Parameter$()
Parameter$( Array Count(Parameter$()) ) = Trim$(Get Split Word$(T), " ")
Next T
rem Isolate paramater data types only ("int", "float")
Dim DataType$() as String
For T = 0 to Array Count(Parameter$())
Split String Parameter$(T), " "
Array Insert At Bottom DataType$()
DataType$(Array Count(DataType$())) = Trim$(Get Split Word$(1), " ")
Next T
// Handle optional parameters - where is first = sign?
// ASSUME - optional params start after all default params
Optional = Array Count(Parameter$())
For T = 0 to Array Count(Parameter$())
if instr( Parameter$(T), "=" )
Optional = T - 1
T = Array Count(Parameter$()) + 1
endif
Next T
While Optional <= Array Count(DataType$())
// Construct the String Table entry line
Inc g_nStringTableEntries
StringTableEntry$ = " " + PadRight$(Str$(g_nStringTableEntries), 3) + " " + Quote$() + DBPFuncName$
if ReturnType$ = "void"
StringTableEntry$ = StringTableEntry$ + "%"
else
StringTableEntry$ = StringTableEntry$ + "[%" + FormatParameterType(ReturnType$)
endif
// Add parameter list
For T = 0 to Optional
StringTableEntry$ = StringTableEntry$ + FormatParameterType(DataType$(T))
Next T
StringTableEntry$ = StringTableEntry$ + "%"
// Add the C++ name
Found = 0
For T = 0 to Array Count(DecoratedName$())
If CPPFuncName$ = Mid$(DecoratedName$(T), 2, Fast Len(CPPFuncName$) )
StringTableEntry$ = StringTableEntry$ + DecoratedName$(T)
T = Array Count(DecoratedName$()) + 1
Found = 1
endif
Next T
// If no decorated name found, abort so nothing is written to file
// Reduce Number of string table entries (was previously incremented)
If Found = 0
Dec g_nStringTableEntries
ExitFunction
endif
StringTableEntry$ = StringTableEntry$ + Quote$()
// Write the table entry to the file
Write String g_StringTableFileNum, StringTableEntry$
Inc Optional
EndWhile
UnDim Parameter$()
UnDim DataType$()
EndFunction
Function FormatParameterType(P$)
Result$ = ""
if P$ = "int" then Result$ = "L"
if P$ = "float" then Result$ = "F"
if P$ = "LPSTR" then Result$ = "S"
if P$ = "double" then Result$ = "O"
if P$ = "LONGLONG" then Result$ = "R"
if P$ = "DWORD" then Result$ = "D"
if P$ = "void" then Result$ = "0"
EndFunction Result$
Note that this code makes extensive use of IanM's Matrix1 Utility plugins. A compiled exe and associated files are available for download in the attachment. It's pretty straightforward to use:
How to use:
1) Open the folder where "String Table Generator.exe|" is located
2) Copy in to this folder all relevant C++ code files
3) Open the file "DecoratedNames.txt" (in this folder), and paste in the list of decorated names from the DLL
4) Run the EXE.
5) You can specify which ID number the string table list will start at - type an integer >= 0.
6) Wait for program to finish (may take a while - please be patient)
7) The program will produce a file "StringTable.rc" - include this with your C++ project and recompile.
Known issues:
1) Program does not recognise /* */ comment blocks - if you've commented out a function like this, you may still get a String Table entry.
2) The program will fail to recognise a C++ declaration typed like this:
__declspec(dllexport) void DG_Create_Checkbox(int FormID, int ID)
It instead expects to find this at the top of the code
#define EXPORT __declspec(dllexport)
(where EXPORT can be any text string) and this as the function declaration:
EXPORT void DG_Create_Checkbox(int FormID, int ID)
We spend our lives chasing dreams. Dark Basic lets us catch some of them.