首页 | 博客群 | 公社 | 专栏 | 论坛 | 图片 | 资讯 | 注册 | 帮助 | 博客联播 | 随机访问
诺基亚702T + 北京神州行cmwap无限流量卡- -| 回首页 | 2012年索引 | - -Python的控制台输出语句print的一个bug

[转载+评论]NSIS插件编写入门

                                      

原文:http://clseto.mysinablog.com/index.php?op=ViewArticle&articleId=1910084



Many schools taught programming but very few of them taught deploying software. You may be so concentrated while programming your great software and haven't felt the difficulties of deploying it to others' computer. If you really want to deploy your software to general public, I would like to tell you: try to spend several days (or even weeks) to write and test the installer of your great software.

Very often, your software requires some prerequisites to run and your installer will try to check it, download some required components and do some post configurations. There are quite a bunch of logics behind and the installer is a completely distinct project from your original program !

I had spent several days to study NSIS. Why choose NSIS ? It is popular and free of charge. However, it only targets Microsoft Windows platform. You may feel annoying at first about NSIS: "Oh ... my God ! I just want to deploy a few files. I don't want to learn a new language to do such stupid stuff." Yes, that's right. If your program has only one exe, that's a waste of time to do so. But if your installer really need some checking like those previously stated, you will find that NSIS is worth to learn.

Here to mind you one important thing: The NSIS you can download from http://nsis.sourceforge.net/Main_Page is an ANSI version. There's another Unicode version at http://www.scratchpaper.com/ (but the installer made by this Unicode version can't be run on Windows 9X).

There are already many tutorials on how to write an NSIS installer so I don't want to repeat. Here, I would like to talk about writing a plugin to extend the functionalities of an installer ....

Actually, there's already an example plugin at "C:Program Files\NSIS\Unicode\Examples\Plugin" (depends on where you install NSIS), you can take a look at it.

Background knowledge 

NSIS plugin is nothing but a DLL exporting one or several C functions (one function one functionality) and which has this kind of prototype:

__declspec(dllexport) void mypluginfunc(HWND hWndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra);

It uses normal C function calling convention and has no return value. No return value ! So, how to communicate with the script ? Use the stack (not the OS process level stack but a stack maintained internally by the NSIS virtual machine) or those predefined global variables ($0, $1, ..., $9, $R0, $R1, ..., $R9. NSIS calls them registers). Also, all parameters on the stack are in string format. To pass an integer, a conversion is needed. Inefficient ? Yes, it is but we are not writing a real-time 3D shooting game.

What's that pointer pointed to by "extra" above ? I don't know either. This testing plugin won't make use of it. Try to find it out yourself.

Plugin step by step

(1) 

I usually use Visual studio to write C programs. So launch a new dll project which exports some symbols. Before writing any code, first change the Runtime library: Project -> Properties -> C/C++ -> Code Generation -> Runtime library. For debug mode, choose "Multi-threaded Debug (/MTd)" whereas release mode, choose "Multi-threaded (/MT)". If not doing so, you will need to deploy the C runtime together with your installer.

(2)

Copy the following files from "C:\Program Files\NSIS\Unicode\Examples\Plugin" and include them to your working project:

api.h, nsis_tchar.h, pluginapi.h, pluginapi.lib

They are the necessary header and object files. Set your project to link with "pluginapi.lib". The functions inside pluginapi.lib are declared in pluginapi.h.

(3)

Try to write our testing plugin C function like this:

extern "C" {

// This is an example of an exported function.
MYPLUGIN_API void mypluginfunc(HWND hWndParent, int string_size,
                               TCHAR *variables, stack_t **stacktop,
                               extra_parameters *extra)
{
    EXDLL_INIT();

    WCHAR szComponent[256];
    popstring(szComponent);

    int len = (int)wcslen(szComponent);

    // make a little change to input parameter
    for (int i = 0; i < len; ++i)
       szComponent[i] += 1;

    // push back on the stack
    pushstring(szComponent);
}

}

Remeber to wrap the function by 'extern "C"', otherwise, VC "thinks" it is a C++ function (since it is inside a cpp file) and hence function name decoration may result.

In the function, first insert the macro "EXDLL_INIT()", which sets up the stack and other parameters.

The rest of the function is just to pop the string passed in, change it a little bit and push back on the stack so that caller can get the modified string.

(4)

Compile the DLL (debug or release version). Then copy the DLL to NSIS plugin directory: "C:\Program Files\NSIS\Unicode\Plugins"

(5)

Lastly, write a testing NSIS script to test this plugin:

OutFile "helloworld.exe"

# default section
Section

   MessageBox MB_OK "Hello world ! Yes..."
   myplugin::mypluginfunc "FDFDqqq我們"
   Pop $0
   MessageBox MB_OK "ret = $0"

SectionEnd

Save the script as "hello.nsi" and compile it with "C:\Program Files\NSIS\Unicode\makensis" hello.nsi".

(6)

Try to run the resulting "helloworld.exe". Since it is a Unicode version, you can pass non-English string to the plugin and see the modified string (2 chinese characters as above). But to mind you that you have to save the script in Unicode format (i.e. from notepad -> save as -> Encoding -> Unicode).

A screenshot to display the modified string (the 2nd MessageBox above):

Picture

Source of the above example: 2197742-mynsisplugin.zip

Enjoy!


我的补充:

1、你从NSIS网站上也能下载到NSIS Plugin Example。但是那个for是NSIS ANSI的。如果要开发for NSIS Unicode的,得从NSIS Unicode的安装目录拿东西。

2、函数的传入参数string_size既不是你从NSIS传入参数的字符串的个数,也不是字符串的长度。而是NSIS编译时的一个常量的值。这个值在NSIS ANSI版里固定为1024,在NSIS Unicode版里上升到8196。参见http://www.scratchpaper.com/home/faq,“Where's the large string version of the Unicode NSIS?”。

3、传入参数是在程序里用popstring(LPTSTR)获取的。如果你是用popstring、pushstring等函数,且在函数参数处用TCHAR来修饰variables,则编译出来的dll文件只能用于ANSI或Unicode二者其一。也许通过强制使用PopStringA和PushStringA可以让一个dll适应ANSI和Unicode,但是我没试过。参见pluginapi.h。

4、传入参数在程序里popstring的时候是从左到右获得的。例如传入参数是pluginName::functionName "123" "456",第一次popstring会得到123,第二次456。

【作者: APOO】【访问统计:】【2012年01月16日 星期一 11:52】【注册】【打印

搜索

Google

Trackback

你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=7028786

回复

验证码:   
评论内容: