Archive: NSIS Idea - Comments Wanted


NSIS Idea - Comments Wanted
Sorry the idea is a little hard to explain so instead of posting a huge chunk of text here I made a little web page for it.

Please please please take the time to read this and post your comments back here. I think this is a very useful feature for NSIS that could also be taken a little further.

Visit the web page about the idea.


Hello Sunjammer,

your idea is good. Can you make a little HowTo for programm a plugin-dll for NSIS? What is possible to add via a dll to NSIS?

What do you think about to add encryption of the compressed data
to NSIS.


(Please reply to my NSIS-GUI thread)


Fiffi:)


Return values, syntax
When still calling the dll with the CallInstDll, the dll knows necessary handles and can push to the stack. I think that is the best way to handle output.

BTW, to be honest, the idea is nice, might be quite a lot of coding to actually get auto-include to work and what is the payoff? We can reduce NSIS script code by a few lines. Are you sure you want to do this? The current approach does not need to consume variables for temp-files by the way. It is safe but not strictly necessary.

Good luck, still a nice idea to get going with the development of NSIS and I like that, but we should take care that the costs do not outweight the payoff.

-Hendri.

Hendri, it all works already but before including it in any release or furthering it I wanted some feedback, thanks.

The payoff I feel is far less cluttered code. (btw the implementation actually does what CallInstDLL does underneath so dlls work the same way as before, and you don't have to use the auto-push).

Also, it transparently handles packing of the dll, temporary filenames and auto-deletion of the dlls used. I find extension dlls in NSIS to be very handy but awkward to use, so I want to make them easier to use.

My changes don't add much in terms of size to the generated NSIS installer but functionally I feel it is very useful. It is only a first attempt at the idea though so maybe it's not a good idea, or maybe it could/should be done differently, or extended... tell me! :D

Fiffi: I have not created the plugin idea, NSIS already supports that, I'm just making it easier to use (I think). The contrib\ExDll directory has a bare-bones example C++ NSIS plugin. Also, I'm not talking about ways to extend NSIS at the moment, e.g. encryption, so ask that as another thread please (but personally I have no need of that feature, maybe others do?).


:up: Good idea!


Great feature!
This is a really useful feature. One of my installers uses 3 different extension DLLs and the code looks a little bit ugly so this feature helps me a lot.
I've just discovered that this feature has been commited to the nsis2k CVS repostitory, I'll test it asap.

BTW, what about the following scenario: Imagine that there are two DLLs which export a function with the same name and your script tries to use this function from one of the DLLs, what will happen in this case?
Compiling will fail in this case (if the feature is correctly implemented)

If you don't have a solution for this problem, here's mine:
It should be possible add a prefix to the function to tell the compiler which DLL to use, like
mydll.dll::function or something similar.


The problem had occured to me, I was waiting to see if opinion about the idea was favourable before taking it any further. I must admit I like your prefix idea, the only brief thought I'd had was a different approach, to have an alias command in the script to provide an alternate name for a command before it is used anywhere, say at the top of the file.

I want to standardise the return mechanism too but that has some issues. For example, I can't assume that the stack contains a return value because if it doesn't and I pop the top of the stack I could mess up things. Two ideas spring to mind:

Firstly some kind of ability to designate a target for a stacked return value, perhaps something like Command=$0 arg arg etc. A second idea is a new variable to contain a return value. The second case would be problematical because I couldn't assume that the stack contained a return value, and yet if I wait until the user uses the new variable it might be too late to pop the return value off the stack.

Of course this isn't an issue for dll functions that set user variables directly rather than use the stack but I don't like them much because they mean you have to program around them in the script remembering always not to use a variable before a call.


Added into NSIS 2.0a4. Great work Sunjammer!


Has anyone other than me tested this yet? Please note that I made it so that this feature is OFF by default so as to minimize any damage it might do were it to be "released" :p


I don't see any solution to the return value problem, either, a DLL function may or may not return a value.
I'm quite happy with the current way return values are handled, I don't think that it needs to be changed.

Another thought: Wouldn't it be better to use 'mynsisdir/bin' instead of ''mynsisdir/dlls'? All default extension DLLs are in the bin directory, the dll one does not even exist.


How about just adding an option to set the search path?


So if I add a command line option to makensis.exe that overrides the search path, and make the default the bin directory that would address both those issues. Speak now or forever hold .. something, or else I'll implement it - tonight probably. (unles KiCHiK beats me to it :igor:)


I don't think it should be a command line switch. Just a normal command. The default search path would be the bin directory and you will be able to specify more search paths with the new command.


I've found two rather annoying bugs.

First one:
Just try to call an extension DLL twice, it will fail.
I guess that the DLL will be deleted after the first call and the next time it can't find the DLL.

Either extract and delete the DLL on Installer start/shutdown (before the first and after the last call) or before and after each call. Mixing this is fatal.
Test it using the InstallOptions example and go back and forth several times, the new dialog will only appear once.

Second one:
Extract the attached dll from the attached .zip file to the dlls folder. With CallInstDLL I could call the function extractall, without the compiler can't find this function.


Just try to call an extension DLL twice, it will fail.
I guess that the DLL will be deleted after the first call and the next time it can't find the DLL.
My test case calls the same function 10 times in a row. The dll is not deleted until the installer exits.

Extract the attached dll from the attached .zip file to the dlls folder. With CallInstDLL I could call the function extractall, without the compiler can't find this function.
When MakeNSIS ran did it list the function at the top when it was dumping detected plugins? This part of the code is immature, it's not really a bug but the PE/COFF file format (which a binary dll is) is incredibly hard to read so I currently only support my test file which all DLLs probably do not look like.

The code was meant to be a proof of concept with an adjustment period afterwards, hence my "Has anyone else tested this other than me?" comment, and hence why I didn't make available a compiled version with the code in, and why it defaults to OFF.

I'll look at these issues later tonight when I get a chance (i'm at work right now). Thanks for attaching the zip.

MakeNSIS did not list the function(s) of my dll.

After some debugging I've found the problem for the function not showing up: In ExternalCommands::GetExports you're reading the offset of the peheader:

unsigned char peheaderoffset = dlldata[0x3c];
Here you assume that the offset is only 1 byte.
In fact it is at least 2 bytes long, maybe 4 (I'm not sure)
This is how I've fixed this problem:
unsigned int peheaderoffset = dlldata[0x3c] + dlldata[0x3d] * 256;

For the other problem, I've not found a solution. Apparently the InstallOptions example failes after clicking a lot of time on back and next and after a while the dialog does not show up anymore. When using CallInstDLL, I can click hundreds of time on back and next without problems.


Thanks. I've just got home so I'm going to resolve it now.

[edit]I've added a PluginDir command... word of advice, don't be a stupid muppet like me and test it by saying ...

PluginDir "c:\winnt\system32\"

I'm still waiting for it to stop listing functions in dlls :D ... which brings me back to the problem of it not listing your function, odd since it lists all the windows ones without a hitch and all of my own NSIS dlls...[/edit]

[edit]You said:
unsigned char peheaderoffset = dlldata[0x3c];
Here you assume that the offset is only 1 byte.
In fact it is at least 2 bytes long, maybe 4 (I'm not sure)

This is a problem for me, because the PE/COFF file format specification is pretty poor and it says nothing about the length of that offset. I think since all other offsets in the format are 4 bytes this one probably is too. Give me some credit, I was writing that code until the small hours :D[/edit]


CodeSquid, I've committed to CVS the following :-

- Fixed a bug in the dll export scanner (spotted and cured by CodeSquid)
- Fixed a bug in the installer runtime (spotted by CodeSquid)
- Renamed all usage of ExternalCommand to Plugin
- Added a compile time PluginDir command
- Accidentally enabled NSIS_CONFIG_PLUGIN_SUPPORT by default.. oops. Oh well it can stay like that for now, KiCHiK can decide what to do about that.


Great news! I've tested it and everything works perfectly.

Found a small flaw, though: makenssi.dsp still refers to ExternalCommand.cpp instead to Plugin.cpp


ROFL I forgot about that, that can be fixed now, at least it wasn't a code issue that time :)


Hi Sunjammer,

I like your idea! I'd personally prefer if it could be done just using the existing NSIS commands, but I understand the problem of not wanting to mess up a user variable.

Maybe some new variable(s) could be added which are only used internally by auto-generated NSIS commands or something. Bizarre (slightly unrelated) thought - a new NSIS variable which returns the value currently on the top of the stack (say, "$_"? :D ) - whether or not it should pop it in the process, I'm not sure.

For DLL return values issue, how about something like

MyPlugin "x" "y" "z" > $1 $2

--
Dave Laundon

A lot of what you just said is very similar in to what I said in the second post on the second page of this thread. Take a look at that and then see if you can think of a solution to the problems I mention.


:: Implemented
Implemented CodeSquid's suggested :: syntax to specify which dll contains the comamd to run (in cases where more than one dll has the same command, OR the command is the same name as a built-in command).

Usage is exactly the same as before unless you wish to be more specific about the dll which contains the command in which case you could say :-

myInstallOptions.dll::dialog $7


I think just the fact that syntax such as "MyPlugin A B C > $1" is being used should be enough to assume that the DLL is going to do the right thing. If the DLL is not designed that way then the script writer should not be using that syntax.

My rambling about $_ was unrelated to plugin support, but yes, I missed your mention of a "variable to contain a return value". So much on the NSIS board tonight - 'tis a lot to take in :) My idea for $_ would be to be able to use the value on the top of the stack in string expressions without popping it, and it would not be an error to use $_ if the stack is empty, it would just return nothing.

Anyway, I look forward to see how this all develops!

Dave.


There is a problem with the MyPlugin A B C > $1 syntax, how does the compiler know that > and $1 are not validate arguments rather than special syntax. If it always treats that style as special syntax you wouldn't be able to pass those values as arguments.

This is why I suggested MyPlugin=$1 A B C, that can be detected without any problems (assuming that =$1 is never going to be part of a valid function name). That syntax is a bit odd though, but then most NSIS syntax is odd in my book (closer to assembler than anything else that springs to mind).

As for $_... I'll think about it :up: (you're not a perl programmer by any chance are you :p)

[edit]Thinking about it I should probably adopt something like MyPlugin /out=$1 A B C since that syntax already exists in NSIS... again it has the "is this a parameter or flag" problem but this is more standard (in NSIS) at least.[/edit]


Agreed about MyPlugin A B C > $1. My only reason for that particular style over yours was the possibility of multiple return values (e.g. PluginX A B C > $1 $2). Perhaps PluginY=$1,$2 A B C. $1,$2=PluginZ A B C would perhaps be more user-friendly, but harder for the compiler to parse I guess?

Perl? Actually I'm an extreme novice in that department, but if such a variable is ever created, it just seems like an obvious choice :D

Thinking about it again, it does not add anything that can't be done already, so no need, whereas what you're doing does actually add extra functionality (automatic extraction/clearup of the DLL).

Dave.


How about Pascal style? Func(parm1, parm2, parm3):$1 $2

[edit]On second thought, that would ruin the entire assembler look... The script would look corrupted[/edit]


Yeah the reason I started this thread in the first place was I wasn't sure if I was really adding anything... it does make using an extension dll a lot sweeter, at least until you have to unstack return values (if the function uses the stack).

The only reason I mentioned perl is that $_ is a well known special perl variable :)

Pascal style? Eeek looks like I've given people the syntax bug :p


Documentation
Here is a link to a modified version of the makensis.htm that contains information on how to use plugins, both the old CallInstDLL way and my new more automatic way.

http://cvs.sourceforge.net/cgi-bin/v...is.htm?rev=1.3

This is not a final version nor will it necessarily be included in the next release by KiCHiK but it should be useful to some people for now at least.