mardi 13 octobre 2009

NSIS - Create a C/C++ Plugin (NSIS DLL) using external DLL function calls

Issue:
Trying to compile a NSIS script using a custom plugin (NSIS DLL) which should be able to use an external DLL functions, I faced some troubles: my script compiled but nothing happened each time my plugin called my external DLL fucntions. 

Reason :
As my plugin needed the external LIB file to compile correctly, my NSIS script needed NSIS to know this external DLL as well. Copying the NSIS DLL file into the NSIS program folder was not sufficient.

Solution:
Before copying the NSIS DLL into C:\Program Files\NSIS\Plugins
I needed to indicate my NSIS compiler which dll to include before compiling the script.
This operation must be done in the .onInit function


Function .onInit
    initpluginsdir
   file /oname=$PLUGINSDIR\libcurl.dll "libcurl.dll"
    file /oname=$PLUGINSDIR\pacparser.dll "pacparser.dll"
    file /oname=$PLUGINSDIR\nspr4.dll "nspr4.dll"
    file /oname=$PLUGINSDIR\js3250.dll "js3250.dll"
    file /oname=$PLUGINSDIR\mozcrt19.dll "mozcrt19.dll"

...

...
...       
FunctionEnd

LANG - Compile C++ Project using a C library (DLL)

Issue:
Trying to compile an Adobe Acrobat API plugin (C++) , I got these LINK error

Error 2 error LNK2019: unresolved external symbol "int __cdecl url_fclose(struct fcurl_data *)" (?url_fclose@@YAHPAUfcurl_data@@@Z) referenced in function "public: char * __thiscall CAdminData::readScriptFromURL(class Str)" (?readScriptFromURL@CAdminData@@QAEPADVStr@@@Z) AdminData.obj

Error 3 error LNK2019: unresolved external symbol "char * __cdecl url_fgets(char *,int,struct fcurl_data *)" (?url_fgets@@YAPADPADHPAUfcurl_data@@@Z) referenced in function "public: char * __thiscall CAdminData::readScriptFromURL(class Str)" (?readScriptFromURL@CAdminData@@QAEPADVStr@@@Z) AdminData.obj

Error 4 error LNK2019: unresolved external symbol "int __cdecl url_feof(struct fcurl_data *)" (?url_feof@@YAHPAUfcurl_data@@@Z) referenced in function "public: char * __thiscall CAdminData::readScriptFromURL(class Str)" (?readScriptFromURL@CAdminData@@QAEPADVStr@@@Z) AdminData.obj
Error    5    error LNK2019: unresolved external symbol "struct fcurl_data * __cdecl url_fopen(char const *,char const *)" (?url_fopen@@YAPAUfcurl_data@@PBD0@Z) referenced in function "public: char * __thiscall CAdminData::readScriptFromURL(class Str)" (?readScriptFromURL@CAdminData@@QAEPADVStr@@@Z)    AdminData.obj

Reason:
Since my Adobe Acrobat plugin was a C++ project whereas my custom DLL was written in C , my code could not compile without indicating explicitly my compiler to consider my included sources (.h header) as a C code piece.

Solution:
#ifdef __cplusplus
   extern "C" {
#endif
...
static int fill_buffer(URL_FILE *file,int want,int waittime);
static int use_buffer(URL_FILE *file,int want);
URL_FILE * url_fopen(const char *url,const char *operation);
int url_fclose(URL_FILE *file);
int url_feof(URL_FILE *file);
size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file);
char * url_fgets(char *ptr, int size, URL_FILE *file);

...

#ifdef __cplusplus
    }
#endif

LINK - Linking PacParser library (DLL) on Visual Studio 2005

Issue:
Trying to write an Adobe Acrobat plugin (API) which should use an external DLL (PACPARSER),
I got these LINK errors:

Error    7    error LNK2019: unresolved external symbol _pacparser_cleanup referenced in function "public: void __thiscall CAdminData::getProxyFromScript(char *,class Str,class Str)" (?getProxyFromScript@CAdminData@@QAEXPADVStr@@1@Z)    AdminData.obj    
Error    8    error LNK2019: unresolved external symbol _pacparser_find_proxy referenced in function "public: void __thiscall CAdminData::getProxyFromScript(char *,class Str,class Str)" (?getProxyFromScript@CAdminData@@QAEXPADVStr@@1@Z)    AdminData.obj    
Error    9    error LNK2019: unresolved external symbol _pacparser_parse_str referenced in function "public: void __thiscall CAdminData::getProxyFromScript(char *,class Str,class Str)" (?getProxyFromScript@CAdminData@@QAEXPADVStr@@1@Z)    AdminData.obj    
Error    10    error LNK2019: unresolved external symbol _pacparser_init referenced in function "public: void __thiscall CAdminData::getProxyFromScript(char *,class Str,class Str)" (?getProxyFromScript@CAdminData@@QAEXPADVStr@@1@Z)    AdminData.obj
    

Reason:
I did not add the right LIB (pacparser.lib) to my project library inputs

Solution:

Linking in pacparser on VisualStudio 2005 is as easy as linking in any other C library. I could do it in following steps;

/* IGNORE IF YOU COMPILE YOUR OWN LIB  
Download compiled binaries (version >1.0.1) for Win32 from
pacparser download page. Direct link to 1.0.1 binaries: click here.
Extract these files on your machine. On extracting it will create a folder
named pacparser-x.y.z (pacparser-1.0.1 for version 1.0.1).
Here are some important files inside it and their function:
IGNORE IF YOU COMPILE YOUR OWN LIB  */

pacparser.dll -> This file is needed to run any application linked against pacparser. This file should be in your path somewhere or in the same directory as your executable.

js3250.dll and nspr4.dll -> pacparser.dll in turn depends on these dlls. These files should also be in path.

pacparser.lib -> Import library. This file is needed to link in pacparser on Visual Studio.

libpacpaser.a -> Import library. This file is needed to link in pacparser using mingw compiler tools.

Copy pacparser.lib and pacparser.h to your Visual Studio project directory.

Modify your project's properties in Visual Studio so that compiler can find and use pacparser.h and pacparser.lib. If you don't know Visual Studio that well (I don't, for example), then follow steps 3 and 4 in the document here.

And then just compile (F7).

Now if you get an error during execution that pacpaser.dll could not be found in your path, that means that your dlls are not in path. To make your application run, you can simply copy above-mentioned 3 dlls - pacparser.dll, js3250.dll and nspr4.dll to the directory where your application's executable is. Alternatively, you can add pacparser's directory (e.g. C:\tools\libs\pacparser-1.0.1) to %PATH% environment variable in "My Computers -> Right Click -> Properties -> Advanced -> Environment Variables".