myBase Software Developer Kit

myBase Software Development Kit (SDK) provides the documentation, samples, C++ header files, library binaries, demo programs and relevant materials that you need to create myBase plugins/addons and standalone applications. The SDK is free for any developers making myBase plug-ins and add-ons. We prepared a few lines of sample codes for you to preview the usage of myBase SDK and the way how to make myBase plug-ins and add-ons.

Sample Code #1

The code snippet listed below demonstrates how to open an SSG based database file to obtain a database handle, and then save changes, and finally close it.

#include <io.h>
#include <iostream>
#include "ssg_dll.h"

_CWrapperSSGDLL ssgdll("ssg.dll"); //load the ssg.dll and import API functions.

static char* __stdcall _GetPasswdCallback(const char* fullpath, const char* hint, int id)
{
	//you may give a popup window here, so end-users can supply a password if required.
	//if you do, you should give a different prompt message for either database or ssg folders;
	//if you don't use the password option, simply return NULL;
	return NULL;
}

int main(void)
{
	const char* dbfile="./dbfile0.nyf"; //the file must exist;

	if(access(dbfile, 0)<0){
		//first create the new file if it doesn't exist;
		FILE* pf=fopen( dbfile, "w+" );
		if(pf){
			fclose(pf);
		}
	}

	H_DB hdb=ssgdll.ssg_open(dbfile, false, ::_GetPasswdCallback);
	if(hdb!=NULL){

		//do something here ...

		//if you made changes to the database, save the changes here;
		ssgdll.ssg_save(hdb);

		//should close the database before exiting;
		ssgdll.ssg_close(hdb);

		std::cout << "Successfully opened the database " << dbfile << std::endl;

	}else{
		std::cout << "Failed to open the database " << dbfile << std::endl;
	}

	return 0;
}

Sample Code #2

The code snippet listed below demonstrates how to query the states/properties of an open SSG database.

#include <io.h>
#include <iostream>
#include "ssg_dll.h"

_CWrapperSSGDLL ssgdll("ssg.dll"); //load the ssg.dll and import API functions.

int main(void)
{
	const char* dbfile="./dbfile0.nyf"; //the file must exist;

	if(access(dbfile, 0)<0){
		//first create the new file if it doesn't exist;
		FILE* pf=fopen( dbfile, "w+" );
		if(pf){
			fclose(pf);
		}
	}

	H_DB hdb=ssgdll.ssg_open(dbfile, false, NULL);
	if(hdb!=NULL){

		bool bModified=ssgdll.ssg_ismodified(hdb);
		std::cout << "The DB is " << (bModified ? "Dirty" : "Clean") << ".\n";

		bool bReadonly=ssgdll.ssg_isreadonly(hdb);
		std::cout << "The DB is open as " << (bReadonly ? "ReadOnly" : "Writable") << ".\n";

		//do something else here ...

		//if the database changed, save the changes;
		if(bModified) ssgdll.ssg_save(hdb);

		//should close the database before exiting;
		ssgdll.ssg_close(hdb);
	}

	return 0;
}

Sample Code #3

The code snippet listed below demonstrates how to create SSG folders and import/export SSG files. The sample code creates a SSG database file and inserts a file, and then export the file. After the file is inserted, we suggest that you open the generated database file with myBase, to see if the SSG file is inserted correctly.

#include <io.h>
#include <iostream>
#include "ssg_dll.h"

_CWrapperSSGDLL ssgdll("ssg.dll"); //load the ssg.dll and import API functions.

int main(void)
{
	const char* dbfile="./dbfile0.nyf"; //the file must exist;

	if(access(dbfile, 0)<0){
		//first create the new file if it doesn't exist;
		FILE* pf=fopen( dbfile, "w+" );
		if(pf){
			fclose(pf);
		}
	}

	H_DB hdb=ssgdll.ssg_open(dbfile, false, NULL);
	if(hdb){
		const char* ssgpath="\\organizer\\data\\item0\\";
		H_FOLDER hfolder=ssgdll.ssg_get_folder(hdb, ssgpath, true);
		if(hfolder){
			const char* ssgfile="\\Organizer\\data\\item0\\test_ssg_file.ini";
			const char* winfile="C:\\windows\\win.ini";
			int nBytes=ssgdll.ssg_import_file(hdb, ssgfile, winfile);
			if(nBytes<0) std::cout << "Failed to import the disk file\n";

			//after this, you'll see the 'win.ini' file when you load it with myBase.

			//now try to export the SSG file and save as another disk file;
			winfile="./win-0.ini";
			nBytes=ssgdll.ssg_export_file(hdb, ssgfile, winfile);
			if(nBytes<0) std::cout << "Failed to export the SSG file\n";

			//after this, you'll see the 'win-0.ini' file.
		}

		//do something else here ...

		//save the changes if modified;
		if(ssgdll.ssg_ismodified(hdb)) ssgdll.ssg_save(hdb);

		//close the database before exiting;
		ssgdll.ssg_close(hdb);
	}
	return 0;
}

Sample Code #4

The code snippet listed below demonstrates how to enumerate child SSG entries under a SSG folder. This sample code only prints the found child entries on the screen. The next example gives a more complex code which enumerates child entries recursively.

#include <io.h>
#include <iostream>
#include "ssg_dll.h"

_CWrapperSSGDLL ssgdll("ssg.dll"); //load the ssg.dll and import API functions.

static bool __stdcall _EnumEntryCallback(bool bFolder, const char* ssgname, const char* hint
	, __TEntryAttr attr, void* userdata)
{
	std::cout << (bFolder ? "Folder Entry:" : "File Entry:");
	std::cout << "  name=" << ssgname;
	std::cout << "  hint=" << hint;
	std::cout << "  attr=" << (int)attr;
	std::cout << "  data=" << (void*)userdata;
	std::cout << std::endl;
	return true;
}

int main(void)
{
	const char* dbfile="./dbfile0.nyf"; //the file must exist;

	if(access(dbfile, 0)<0){
		//first create the new file if it doesn't exist;
		FILE* pf=fopen( dbfile, "w+" );
		if(pf){
			fclose(pf);
		}
	}

	H_DB hdb=ssgdll.ssg_open(dbfile, true, NULL); //open as readonly here;
	if(hdb!=NULL){
		const char* ssgpath="\\organizer\\data\\item0\\";
		bool bSucc=ssgdll.ssg_enum_entries(hdb, ssgpath
				, ::_EnumEntryCallback, false, (void*)NULL);

		//do something here ...

		//should close the database before exiting;
		ssgdll.ssg_close(hdb);
	}
	return 0;
}

Sample Code #5

This example gives a a little bit more complex code which enumerates child entries recursively and prints all entries on console. In order to enumerate all child entries recursively, we made a few helper classes. The CSsgEntryInfo class describes attributes of an SSG entry. The _CSsgEntries is a container class, each item of which is an object of type CSsgEntryInfo.

In the sample code, we first have all found sub folders under a SSG folder temporarily saved in an instance of the container class, then walk through the container and print every folder's caption text on the console. The folder's caption text are indented with 4 blank spaces. And then go on enumerating each sub folders.

#include <io.h>
#include <iostream>
#include "ssg_dll.h"

_CWrapperSSGDLL ssgdll("ssg.dll"); //load the ssg.dll and import API functions.

struct _CSsgEntryInfo{

	bool		m_bFolder;
	std::string	m_sName;
	std::string	m_sHint;
	__TEntryAttr	m_iAttr;

	_CSsgEntryInfo(bool bFolder=FALSE
		, const char* name=NULL
		, const char* hint=NULL
		, __TEntryAttr attr=0)
		: m_bFolder(false)
		, m_sName(name?name:"")
		, m_sHint(hint?hint:"")
		, m_iAttr(attr)
	{
	}

	_CSsgEntryInfo(const _CSsgEntryInfo& d){*this=d;}

	const _CSsgEntryInfo& operator=(const _CSsgEntryInfo& d)
	{
		m_bFolder=d.m_bFolder;
		m_sName=d.m_sName;
		m_sHint=d.m_sHint;
		m_iAttr=d.m_iAttr;
		return *this;
	}

};

class _CSsgEntries : public std::vector<_CSsgEntryInfo>{

private:

	unsigned		m_iEntryType;

public:

	enum{
		  file=1
		, folder=2
		, any=file|folder
		, all=any
	};

	unsigned _gettype(void)const{return m_iEntryType;}

public:

	_CSsgEntries(unsigned iEntryType=any) : m_iEntryType(iEntryType){return;}

};

static bool __stdcall _EnumEntryCallback(bool bFolder, const char* ssgname, const char* hint
	, __TEntryAttr attr, void* userdata)
{
	_CSsgEntries* pvItems=(_CSsgEntries*)userdata;
	if(pvItems){
		bool bAdd=false;
		unsigned type=pvItems->_gettype();
		if(bFolder){
			if( type & _CSsgEntries::folder ) bAdd=TRUE;
		}else{
			if( type & _CSsgEntries::file ) bAdd=TRUE;
		}
		if(bAdd){
			pvItems->push_back(_CSsgEntryInfo(bFolder, ssgname, hint, attr));
		}
	}
	return true;
}

void _print_sub_entries(H_DB hdb, const char* ssgpath, const std::string& sIndent)
{
	_CSsgEntries vItems(_CSsgEntries::folder);
	ssgdll.ssg_enum_entries(hdb, ssgpath , ::_EnumEntryCallback, false, (void*)&vItems);
	_CSsgEntries::const_iterator it;
	for(it=vItems.begin(); it!=vItems.end(); ++it){
		std::string title=it->m_sHint; if(title.empty()) title="New Info Item...";
		std::cout << sIndent << title << std::endl;
		std::string subpath=std::string(ssgpath) + std::string("\\") + it->m_sName;
		::_print_sub_entries(hdb, subpath.c_str(), sIndent+"    "); //recursively...
	}
}

int main(void)
{
	const char* dbfile="./dbfile0.nyf"; //the file must exist;

	if(access(dbfile, 0)<0){
		//first create the new file if it doesn't exist;
		FILE* pf=fopen( dbfile, "w+" );
		if(pf){
			fclose(pf);
		}
	}

	H_DB hdb=ssgdll.ssg_open(dbfile, true, NULL); //open as readonly here;
	if(hdb!=NULL){
		const char* ssgpath="\\Organizer\\data";
		::_print_sub_entries(hdb, ssgpath, "");

		//do something here ...

		//should close the database before exiting;
		ssgdll.ssg_close(hdb);
	}
	return 0;
}

Sample Code #6

This sample code offers a basic framework creating a plugin DLL. It tries to add two menu items onto myBase's main menu.


#include <windows.h>

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;
}

int __stdcall _nyf_addon_validate(int a,int b)
{
	//must export this function, for myBase to examine the plugin DLL;
	return a+b+1; //+1 for nyf5 API Specs.
}

int __stdcall _nyf_addon_query_menuitems(char* buf, int max)
{
	//2006.8.5 prepare description text for the menu layout;
	
	char* entries=

	"\nmenu[0].PopupMenu=MAINMENU.FILE"
	"\nmenu[0].Caption=Sample1 ..."
	"\nmenu[0].Hint=This is a sample plugin for myBase"
	"\nmenu[0].ProcName=_nyf_addon_sample1_func"
	"\nmenu[0].Shortcut=Ctrl+Shift+Alt+1"

	"\nmenu[1].PopupMenu=MAINMENU.HELP"
	"\nmenu[1].Caption=About Sample1 ..."
	"\nmenu[1].Hint=Display info about the sample"
	"\nmenu[1].ProcName=_nyf_addon_sample1_about"
	"\nmenu[1].Shortcut=Ctrl+Shift+Alt+2"

	;

	//put into the return buffer, which receives up to 32KB text;
	::strcpy(buf, entries);

	//total 2 menu items being implemented in this DLL;
	return 2;
}

int __stdcall _nyf_addon_sample1_func(const char* param, char* ret)
{
	::MessageBox(::GetDesktopWindow()
		, "this is a sample plugin"
		, "sample1"
		, MB_OK|MB_ICONINFORMATION);

	return 0;
}

int __stdcall _nyf_addon_sample1_about(const char* param, char* ret)
{
	::MessageBox(::GetDesktopWindow()
		, "Sample Plugin version 1.0\nCopyright 2006 Wjjsoft.com"
		, "about sample1"
		, MB_OK|MB_ICONINFORMATION);

	return 0;
}

Sample Code #7

Within this sample code, you'll see how to extract parameter values from the plugin parameters text. The plugin parameter text consists of several lines, each lines are formatted in the 'key=val' form, and separated with the linefeed '\n'.

#include <string>
#include <map>
#include <iterator>
#include <algorithm>
#include <windows.h>

class _CPluginParams{

public:

	std::string				m_sParams;
	std::map<std::string, std::string>	m_mParams;

protected:

	void _split_params(const char* lpszParam)
	{
		std::string params=lpszParam ? lpszParam : "";
		std::string::size_type p;
		do{
			p=params.find("\n");
			if(p!=std::string::npos){
				std::string line=params.substr(0, p);
				params=params.substr(p+1);
				if(!line.empty()){
					std::string::size_type x=line.find("=");
					if(x!=std::string::npos){
						std::string key=line.substr(0, x);
						std::string val=line.substr(x+1);
						if(!key.empty()){
							m_mParams[key]=val;
						}
					}
				}
			}
		}while(p!=std::string::npos);
	}

	std::string _find(const std::string& key)const
	{
		std::map<std::string, std::string>::const_iterator it=
		m_mParams.find(key);
		return (it!=m_mParams.end()) ? it->second : "";
	}

	struct __ansi_case_conv{
	 private:
		bool bUpper;
	 public:
		__ansi_case_conv(bool b):bUpper(b){return;}
		void operator()(char& c)const{c = bUpper ? ::toupper(c) : ::tolower(c);}
	};

	static std::string __upper(const std::string& str){std::string s=str; std::for_each(s.begin(), s.end(), __ansi_case_conv(true)); return s;}
	static std::string __lower(const std::string& str){std::string s=str; std::for_each(s.begin(), s.end(), __ansi_case_conv(false)); return s;}

public:

	_CPluginParams(const char* param) : m_sParams(param)
	{
		_split_params(param);
	}

	std::string _get_str(const std::string& key)const{return _find(key);}

	int _get_int(const std::string& key, int defval)const
	{
		std::string str=_get_str(key);
		if(!str.empty()){
			if(__lower(str).find("0x")==0){
				int val=0;
				if(1==::sscanf(str.c_str()+2,"%x",&val)){
					return val;
				}
			}else{
				return ::atoi(str.c_str());
			}
		}
		return defval;
	}

	HWND _get_hwnd(const std::string& key)const
	{
		return (HWND)_get_int(key, 0);
	}

};

int __stdcall _nyf_addon_validate(int a,int b)
{
	//must export this function, for myBase to examine the plugin DLL;
	return a+b+1; //+1 for nyf5 API Specs.
}

int __stdcall _nyf_addon_query_menuitems(char* buf, int max)
{
	//2006.8.5 prepare description text for the menu layout;
	
	char* entries=

	"\nmenu[0].PopupMenu=MAINMENU.FILE"
	"\nmenu[0].Caption=Sample2 ..."
	"\nmenu[0].Hint=This is a sample plugin for myBase"
	"\nmenu[0].ProcName=_nyf_addon_sample2_func"
	"\nmenu[0].Shortcut=Ctrl+Shift+Alt+3"

	"\nmenu[1].PopupMenu=MAINMENU.HELP"
	"\nmenu[1].Caption=About Sample2 ..."
	"\nmenu[1].Hint=Display info about the sample"
	"\nmenu[1].ProcName=_nyf_addon_sample2_about"
	"\nmenu[1].Shortcut=Ctrl+Shift+Alt+4"

	;

	//put into the return buffer, which receives up to 32KB text;
	::strcpy(buf, entries);

	//total 2 menu items being implemented in this DLL;
	return 2;
}

int __stdcall _nyf_addon_sample2_func(const char* param, char* ret)
{
	_CPluginParams xParams(param);

	//extracts the main frame's Windows Handle;
	HWND hWndParent=xParams._get_hwnd("MainFrame.hWnd");

	//extracts the index of the currently selected database;
	int iCurDb=xParams._get_int("RunningDB.iCurDB", -1);

	//converts the index into a string;
	char sTmp[32]; ::memset(sTmp, 0, sizeof(sTmp));
	::sprintf(sTmp, "%d", iCurDb);

	//make the database's key, like this: RunningDB[0].
	std::string dbkey="RunningDB[" + std::string(sTmp) + "].";

	//extracts the database's file path;
	std::string dbfile=xParams._get_str(dbkey+"sFilePath");

	std::string msg="Current DB File: " + dbfile;

	::MessageBox(hWndParent
		, msg.c_str() 
		, "sample2"
		, MB_OK|MB_ICONINFORMATION);

	return 0;
}

int __stdcall _nyf_addon_sample2_about(const char* param, char* ret)
{
	_CPluginParams xParams(param);

	HWND hWndParent=xParams._get_hwnd("MainFrame.hWnd");

	::MessageBox(hWndParent
		, "Sample Plugin version 1.0\nCopyright 2006 Wjjsoft.com"
		, "about sample2"
		, MB_OK|MB_ICONINFORMATION);

	return 0;
}

Sample Code #8

There's another particular type of programs which could communicate with myBase Desktop but will be running standalone. They're usually invoked from within other applications and can direct myBase Desktop to perform certain operations. For this purpose, myBase Desktop provided a set of APIs, which allows third-party programs to comminucate with myBase Desktop. We called these type of programs 'Addons'. This term is used to differ from the 'Plugins' in case of confusion. For instance, the WebCollect addon is based on the addon APIs, and is invoked from within web browsers and then will be running standalone. WebCollect gives a list of detected myBase instances so end-users can choose one to save the webpages. In order for an addon program to communiate with myBase Desktop, the addon program first needs to detect an instance of myBase Desktop and its frame window handle, so the addon program can send specific messages to the window. This section includes the sample C++ program which tries to demonstrate how to detect myBase instances.

#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <windows.h>

#define		__NYFCLIENT_MAINWINDOW_CLASSNAME	"wjjsoft::nyfclient::mainform"
#define		__NYFEDIT_MAINWINDOW_CLASSNAME		"wjjsoft::nyfedit::mainform"

BOOL CALLBACK _EnumWindowsProc_myBase(HWND hWnd, LPARAM lParam)
{
	std::pair< std::string, std::vector<HWND>* >* pData=(std::pair< std::string, std::vector<HWND>* >*)lParam;

	std::string sClsName=pData ? pData->first : "";
	std::vector<HWND>* pvItems=(std::vector<HWND>*)(pData ? pData->second : NULL);

	if(!pvItems|| !hWnd || sClsName.empty()) return false;

	const int size=64;
	std::vector<TCHAR> buf(size+1, '\0');
	int x=::GetClassName(hWnd, &(buf[0]), size);
	if(x>0){
		if( sClsName == &buf[0] )
		pvItems->push_back(hWnd);
	}
	return true;
}

int _detect_myBase_windows(std::vector<HWND>& vItems)
{
	{
		//detect nyfedit.exe instances
		std::pair< std::string, std::vector<HWND>* > d0(__NYFEDIT_MAINWINDOW_CLASSNAME, &vItems);
		::EnumWindows((WNDENUMPROC)::_EnumWindowsProc_myBase, (LPARAM)&d0);
	}

	{
		//detect nyfclient.exe instances
		std::pair< std::string, std::vector<HWND>* > d0(__NYFCLIENT_MAINWINDOW_CLASSNAME, &vItems);
		::EnumWindows((WNDENUMPROC)::_EnumWindowsProc_myBase, (LPARAM)&d0);
	}
	return vItems.size();
}

struct _CPred_PrintWindowCaption{
	void operator()(HWND hWnd)
	{
		if(::IsWindow(hWnd)){
			int len=1024*4;
			std::vector<char> buf(len+1, '\0');
			::SendMessage(hWnd, WM_GETTEXT, (WPARAM)len, (LPARAM)(&(buf[0])));
			std::string title=&buf[0];
			std::cout << title << std::endl;
		}
	}

};

int main(void)
{
	std::vector<HWND> vItems;
	::_detect_myBase_windows(vItems);

	std::cout << "The following myBase instances detected:" << std::endl << std::endl;

	std::for_each(vItems.begin(), vItems.end(), _CPred_PrintWindowCaption());

	std::cout << std::endl;

	return 0;
}

Any Questions

If you're interested in making your own plug-ins/add-ons, and have any questions please let us know.