Skip to content
⌘ NSIS Forum Archive

Calling managed .NET DLL from NSIS - this works :-)

112 posts

jayfox911#
Originally Posted by rbchasetfb View Post
claesabrandt, very cool that we got through that one. Thanks for sticking with it too. As a thanks, I thought I would post some code that I use in my .NET dll that CLR.dll is calling. It allows the functions in the .NET dll to update the Details list during the install without have to wait to return to the installer and then do a DetailPrint call with some funky string work. The code is in VB.NET, but could easily be converted to C#. The error handling is a little brute force in the MakeLogEntry function, it just ignores the error and returns, potentially writing nothing to the Details list. Non-the-less, very useful I think.

VB.NET Code Required:
Imports System
Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.IO
Imports System.Windows.Forms
Namespace NSIS
    Public Class TestClass
        //LVITEM Structure Declaration.
        <StructLayout(LayoutKind.Sequential)> _
        Public Structure LVITEM
            Public mask As Int32
            Public iItem As Int32
            Public iSubItem As Int32
            Public state As Int32
            Public stateMask As Int32
            Public pszText As String
            Public cchTextMax As Int32
            Public iImage As Int32
            Public lParam As IntPtr
        End Structure
        //SendMessage API declarations.
        Public Declare Function SendMessageLV Lib "user32" Alias _
          "SendMessageA"(ByVal hwnd As IntPtr, _
                         ByVal wMsg As Int32, _
                         ByVal wParam As Integer, _
                         ByRef lParam As LVITEM) As Integer
        Public Declare Function SendMessage Lib "user32" Alias _
          "SendMessageA"(ByVal hwnd As IntPtr, _
                         ByVal wMsg As Int32, _
                         ByVal wParam As Integer, _
                         ByRef lParam As Integer) As Integer                
        //Constants used by LVITEM and SendMessage
        Public Const LVM_FIRST as Integer = &H1000 
        Public Const LVM_GETITEMCOUNT as Integer = LVM_FIRST + 4
        Public Const LVM_GETITEM as Integer = LVM_FIRST + 5
        Public Const LVM_INSERTITEM as Integer = LVM_FIRST + 7
        Public Const LVM_SCROLL as Integer = LVM_FIRST + 20
        Public Const LVIF_TEXT as Integer = &H0001
        Public Const LVIS_FOCUSED as Integer = &H0001
        //Test function for calling from an NSIS installer using CLR.dll
        Public Function ChopString( _
                ByVal hwnd as IntPtr, _
                ByVal stringToChop as String, _
                ByVal chopToLength as Integer) As String
            Dim ret as String = ""
            If stringToChop.Length > chopToLength Then
                MakeLogEntry(hwnd, "Chopping String '" & stringToChop & "'.")
                ret = stringToChop.SubString(0,chopToLength)
                MakeLogEntry(hwnd, "String chopped to '" & ret & "'.")
            Else
                MakeLogEntry(hwnd, "String '" & _
                    stringToChop & _
                    "' to short to chop to length '" & _
                    chopToLength & "'.")
            End If
            Return ret
        End Function
        //Here's the key function that writes to the Details list.
        Public Sub MakeLogEntry(ByVal hwnd As IntPtr, entry as String)
            Try
                //Get current list count
                Dim c as Integer = SendMessage(hwnd,LVM_GETITEMCOUNT,0,0)+1
                //Setup a LVITEM structure for the Insert message
                Dim lv As New LVITEM
                lv.iItem = c
                lv.pszText = entry
                lv.mask = LVIF_TEXT
                lv.stateMask = LVIS_FOCUSED
                lv.state = LVIS_FOCUSED
                //Insert the LVITEM into the list
                c = SendMessageLV(hwnd, LVM_INSERTITEM, 0, lv)
                //Scroll the list so the item is visible
                SendMessage(hwnd,LVM_SCROLL,0,12)
            Catch ex As Exception
                'Do Nothing, just return
            End Try
        End Sub
    End Class
End NameSpace 
NSIS Code with CLR.dll call to test with:
Name "Test CLRDLL MakeLogEntry"
OutFile "TestCLRDLL.exe"
ShowInstDetails show
Var DetailsHWND
Page instfiles
Section
    DetailPrint "Starting Test"
    InitPluginsDir
    SetOutPath $PLUGINSDIR
    File "TestCLRDLL.dll"
    FindWindow $0 "#32770" "" $HWNDPARENT
    GetDlgItem $DetailsHWND $0 1016
    CLR::Call "TestCLRDLL.dll" \
        "NSIS.TestClass" \
        "ChopString" \
        3 \
        $DetailsHWND "Testing Make Log Entry" 9
    Pop $0
    DetailPrint "ChopString Result = $0"
    DetailPrint "Test Complete"
SectionEnd 
Lastly, I've attached a zip file of the TestCLRDLL.dll project for VS2005 and included the TESTCLRDLL.nsi as will. Enjoy. I hope it's as useful for use as it is for me.
This example will not work for me also. Any ideas?
Afrow UK#
Firstly you don't need to extract plug-in DLL's to use them. NSIS handles that itself. Also, if NSIS says a plug-in call is an invalid command then that means your plug-in DLL isn't in the NSIS Plugins folder.

Stu
jayfox911#
Originally Posted by Afrow UK View Post
Firstly you don't need to extract plug-in DLL's to use them. NSIS handles that itself. Also, if NSIS says a plug-in call is an invalid command then that means your plug-in DLL isn't in the NSIS Plugins folder.

Stu
Thank you that worked.
I put the CLR.dll in the \Program Files\NSIS\Plugins\ folder. Just like the second sentence of the CLR wiki says to do. Next time I will RTFM
taralex#
Unicode support

I got stuck for a couple of days when trying to move my installation to a Unicode-based NSIS. All my calls just stopped working. After a vain search for a similar dll, but supporting unicode, I downloaded the original source and upgraded it.
Now this was my first ever experience with c++, so I'm really not sure if I just didn't screw something up, but it worked in a couple of tests I did. I posted the link to the updated code on the Viki page.

I moved all the more or less complex functionality to a .net dll now, because all other means of calling dlls (like System::Call) stopped working in the Unicode release of NSIS.
langdon#
Is anyone capable of recompiling this to add 4.0 support? It'll execute 4.0 assemblies, but will fail if you use any 3.5+ features (like simple Linq statements).
mfiedlerwd#
Hi Claes,

I have already problems on setting/getting Properties.

namespace test
{
public class test
{

// Properties
private string _Name = "";

public string Name
{
get { return _Name; }
set { _Name = value; }
}


public string helloworld()
{
return "Hello " + Name;
}
}
}
I first call the set_name Property, then the helloworld method. But allways the Propety is empty on the second call.

It seems that on every call will generated a new object instance of the dll.

Did you already hav fixed this problem? Or have a solution?

Best Regards,
Michael
Afrow UK#
To avoid incompatibility issues which can occur with different .NET versions I would put your .NET code into a command-line executable and run it using ExecDos.

Stu
mfiedlerwd#edited
Hi Stu, thanks for the suggestion.

Well, when i do a simple call it works fine with an exe or the dll as well.

But when i need to do multiple calls, i think i will have a problem with an exe. (ex. i have to do some database related work and in OLEDB-PLUGIN script files are limited to 60k, so i wrote a .net dll )

Best regards

Michael
ghers#
Plug in doesn't work for me

Hi Claes,

I'm new to NSIS and I'm probably making a lot of beginner's errors...

I tried your plug in and it seems to ignore the /NOUNLOAD setting. When I call my DLL the first time it works, but any time after that I get "object reference not set to an instance of an object" exception. When I try the same DLL with a C# test program it behaves as expected.

Attached is a screenshot of what's happening. I've commented out additional tests as it already fails on the 2nd call. I'll be happy to send you the source code of my DLL.

This DLL is really just a work-around. What I really need is a page like the install log page which shows DetailPrint messages and a progress bar. But I need this page to report findings and progress while I'm scanning the system for installed programs, before the user gets a chance to decide what will be installed and what not. The installer has to handle quite a lot, including external installers for Adobe, Java, Apache, MySQL, some SQL scripts, etc.. So this requires quite a bit of preparation to assess what needs to be installed for a fresh install or for an upgrade of an existing system. Since I can't figure out how to use this page for my purposes I thought that the easiest way would be to create my own in c#. After all it's just a text box and a progress bar.

I would appreciate any advice you can give.
Kind regards, Gilbert
Afrow UK#
You can have more than one InstFiles page. All Sections will be ran for both pages though which means you need to dynamically select/unselect the Sections you want to run before showing the page (i.e. otherwise if you have two InstFiles pages, your Sections will be executed twice). You can use the SelectSection/UnselectSection macros in Sections.nsh for that.

Stu
Jamesn_xD#
Hi all,

I have a little Problem with this issue.

Another issue is that if want to call a .NET dll from your .NET dll, you will find that the installer cannot find the second .NET dll. For the moment the remedy is to wrap your installer in another installer. This other installer should place the .NET dlls in the same directory as the installer will run from. When the installer runs, the .NET dlls can now be found. There will only be distributed one setup file.
I have written a dll in C#, which makes changes on a .config file. In my dll, i use System.xml.dll.

If i have understood the solution correctly, the system.xml.dll must be in the same directory which the Installer.exe. But this isn't working by me.

This is my own dll: (for testing i have only one line)

public void WriteConfigFile(string path, string datasource, string dbName, string user, string pw)
{
XmlDocument doc = new XmlDocument(); //needs System.xml.dll
}
And here is my call in the NSIS script:
Section "Test Write" SEC01
SetOutPath $INSTDIR
File "Test Data\InvoiceConfigurator.exe.config"
InitPluginsDir
SetOutPath $PLUGINSDIR
File "WriteConfigFile.dll"
File "system.xml.dll"
CLR::Call /NOUNLOAD "WriteConfigFile.dll" "WriteConfigFile.WriteClass" "WriteConfigFile" 5\
"$INSTDIR\InvoiceConfigurator.exe.config" "test" "test" "testuser" "testpw"
pop $0
MessageBox MB_OK $0
SectionEnd
The NSIS scipt, Installer, WriteConfigFile.dll and System.xml.dll are in the same folder.

Here is a Screenshot of the Error MessageBox:

Message in english means: "Exception has been thrown by the target of an invocation."

I hope anybody can help me!

Thanks in advance 🙂
Anders#
I don't claim to know anything about how .NET libraries are loaded but perhaps the CLR plugin should have a CLR::PreloadLib function that allows you to build a list of libraries that have to be LoadLibrary'd before it makes the actual call inside CLR::Call...
ashwineec#
CLR.DLL compatibility with Windows 2012

Hello,

We have created a installer using NSIS, but on Windows 2012 if we have a feature .net Framwork 3.5 as disabled then installation fails and gives error in temp file "Unable to Load CLR.DLL in installation log file."

is there any dependency for .net framework 3.5, if no please help us to fix this.
JasonFriday13#
If 2.0 is included within 3.5, that would make sense that the plugin fails if .net is disabled since the plugin relies on 2.0.
r2du-soft#
how can return methods with ref parameter in CLR Plugin?

        private void button1_Click(object sender, EventArgs e)
        {
            int gg = 65;
            string aa = Two_String_Returner(45, ref gg);
            MessageBox.Show(aa);
            MessageBox.Show(gg.ToString());
        }

        public string Two_String_Returner(int a, ref int b)
        {
            b = a + b;
            string Word = "Hello";
            return Word;
        }

i use from ref int in method but i cant read return value in NSIS



when i create dll and use the use from this code:

CLR::Call "ClassLibrary13.dll" "ClassLibrary13.Class1" "Two_String_Returner" 2 "55" "$2"
Pop $0 # return value = exit code
CLR::Destroy
when i pop , get hello in $0
but i cannot return ref int b = "110"
r2du-soft#
I have a problem With The CLR Plugin
i need to call mydll to many times

Var CNTR

Section

STR:

IntOp $CNTR $CNTR + 1

CLR::Call /NOUNLOAD "FTPCityToolkit.dll" "FTPCityToolkit.FTPCityToolkitClass" "FTPMakeDir" 4 "$FTP_Root_Address" "MainFolder/Folder$CNTR" "$FTP_Root_UserName" "$FTP_Root_PassWord"
Pop $0 # return value = exit code

Goto STR

SectionEnd

this Code Create a Folder in Ftp with name MainFolder and inside of that Create Folders Folder1, Folder2 ....
After every run of the loop My application Occupies much more space in memory
also if i use from CLR:😁estroy before Goto STR , application Hangs.
i how can many times use the CLR plugin without Hangs?
r2du-soft#
CLR Plug-in News

New version of CLR Plug-in released

What's new in version 1.0
  • Fix bug memory overflow
  • If you need to call CLR::Call more than once now this is possible (without hang)
  • Removed CLR:😁estroy
r2du-soft#
CLR Plug-in News

New version of CLR Plug-in released

What's new in version 1.1
  • Build with Visual Studio 2005 SP1 Professional On Windows 7 SP1 x86
  • Solve a problem that does not work on some windows (Change compile settings and use from lower .net framework (net 2.0))
tbnorris#
Originally Posted by r2du-soft View Post
CLR Plug-in News

New version of CLR Plug-in released

What's new in version 1.1
  • Build with Visual Studio 2005 SP1 Professional On Windows 7 SP1 x86
  • Solve a problem that does not work on some windows (Change compile settings and use from lower .net framework (net 2.0))
I know this post is quite old, but does this plug-in have an active developer? I have made changes to the plugin for our own usage, and wanted to send those commits back upstream for others. Basically, is there a git repository, or something similar. that would take a code commit?
tbnorris#
I did find this git repo ( https://github.com/ARLM-Keller/nsis-clr ), but it is for the 0.5 version that is on the wiki page still, and this one seems to be much more refined and because the version number is at 1.1, I am assuming that since this one is higher, it is the one that should be continued.
tbnorris#
I noticed what I think was r2du-soft's fork of the CLR 0.5 release
So, I forked, and then did two things:
1. Updated my fork to CLR 1.1
1.1. Dropped r2du-soft's CLR 1.1 zip onto my fork
1.2. Created a pull request for r2du-soft to get his code into his fork
2. Did my needed changes
2.1. Moved to Visual Studio 2019
2.2. Moved to .NET 4.x, because .NET 4.0 released with XP SP3 which is now unsupported by Microsoft, so an assumption of 4.x should be safe now.
2.3. Added a reference to "System.Web.Services" so that assemblies that have "Service Reference" objects can load, I have a .NET 4.7.2 DLL with this situation.

My fork lives here: https://github.com/tbnorris/nsis-clr