I'm extending my Inno-Setup script with code that I can best implement in C# in a managed DLL. I already know how to export methods from a managed DLL as functions for use in an unmanaged process. It can be done by IL weaving, and there are tools to automate this:
NetDllExport (written by me)
UnmanagedExports
So after exporting, I can call my functions from Pascal script in an Inno-Setup installer. But then there's one issue: The DLL can't seem to be unloaded anymore. Using Inno-Setup's UnloadDLL(...) has no effect and the file remains locked until the installer exits. Because of this, the setup waits for 2 seconds and then fails to delete my DLL file from the temp directory (or install directory). In fact, it really stays there until somebody cleans up the drive.
I know that managed assemblies cannot be unloaded from an AppDomain anymore, unless the entire AppDomain is shut down (the process exits). But what does it mean to the unmanaged host process?
Is there a better way to allow Inno-Setup to unload or delete my DLL file after loading and using it?
As suggested in other answers, you can launch a separate process at the end of the installation that will take care of the cleanup, after the installation processes finishes.
A simple solution is creating an ad-hoc batch file that loops until the DLL file can be deleted and then also deletes the (now empty) temporary folder and itself.
procedure DeinitializeSetup();
var
FilePath: string;
BatchPath: string;
S: TArrayOfString;
ResultCode: Integer;
begin
FilePath := ExpandConstant('{tmp}\MyAssembly.dll');
if not FileExists(FilePath) then
begin
Log(Format('File %s does not exist', [FilePath]));
end
else
begin
BatchPath :=
ExpandConstant('{%TEMP}\') +
'delete_' + ExtractFileName(ExpandConstant('{tmp}')) + '.bat';
SetArrayLength(S, 7);
S[0] := ':loop';
S[1] := 'del "' + FilePath + '"';
S[2] := 'if not exist "' + FilePath + '" goto end';
S[3] := 'goto loop';
S[4] := ':end';
S[5] := 'rd "' + ExpandConstant('{tmp}') + '"';
S[6] := 'del "' + BatchPath + '"';
if not SaveStringsToFile(BatchPath, S, False) then
begin
Log(Format('Error creating batch file %s to delete %s', [BatchPath, FilePath]));
end
else
if not Exec(BatchPath, '', '', SW_HIDE, ewNoWait, ResultCode) then
begin
Log(Format('Error executing batch file %s to delete %s', [BatchPath, FilePath]));
end
else
begin
Log(Format('Executed batch file %s to delete %s', [BatchPath, FilePath]));
end;
end;
end;
You could add a batch script (in the form of running cmd -c) to be executed at the end of setup that waits for the file to be deletable and deletes it. (just make sure to set the inno option to not wait for the cmd process to complete)
You could also make your installed program detect and delete it on first execution.
As suggested in this Code Project Article : https://www.codeproject.com/kb/threads/howtodeletecurrentprocess.aspx
call a cmd with arguments as shown below.
Process.Start("cmd.exe", "/C ping 1.1.1.1 -n 1 -w 3000 > Nul & Del " + Application.ExecutablePath);
But basically as #Sean suggested, make sure you dont wait for the cmd.exe to exit in your script.
While not exactly an answer to your question, can't you just mark the DLL to be deleted next time the computer is restarted?
Here's what I did, adapted from Martin's great answer. Notice the 'Sleep', this did the trick for me. Because the execution is called in a background thread, that is not a blocker, and leaves sufficient time for InnoSetup to free up the resources.
After doing that, I was able to clean the temporary folder.
// Gets invoked at the end of the installation
procedure DeinitializeSetup();
var
BatchPath: String;
S: TArrayOfString;
FilesPath: TStringList;
ResultCode, I, ErrorCode: Integer;
begin
I := 0
FilesPath := TStringList.Create;
FilesPath.Add(ExpandConstant('{tmp}\DLL1.dll'));
FilesPath.Add(ExpandConstant('{tmp}\DLL2.dll'));
FilesPath.Add(ExpandConstant('{tmp}\DLLX.dll'));
while I < FilesPath.Count do
begin
if not FileExists(FilesPath[I]) then
begin
Log(Format('File %s does not exist', [FilesPath[I]]));
end
else
begin
UnloadDLL(FilesPath[I]);
if Exec('powershell.exe',
FmtMessage('-NoExit -ExecutionPolicy Bypass -Command "Start-Sleep -Second 5; Remove-Item -Recurse -Force -Path %1"', [FilesPath[I]]),
'', SW_HIDE, ewNoWait, ErrorCode) then
begin
Log(Format('Temporary file %s successfully deleted', [ExpandConstant(FilesPath[I])]));
end
else
begin
Log(Format('Error while deleting temporary file: %s', [ErrorCode]));
end;
inc(I);
end;
end;
Exec('powershell.exe',
FmtMessage('-NoExit -ExecutionPolicy Bypass -Command "Start-Sleep -Second 5; Remove-Item -Recurse -Force -Path %1"', [ExpandConstant('{tmp}')]),
'', SW_HIDE, ewNoWait, ErrorCode);
Log(Format('Temporary folder %s successfully deleted', [ExpandConstant('{tmp}')]));
end;
The easy way to do what you want is through an AppDomain. You can unload an AppDomain, just not the initial one. So the solution is to create a new AppDomain, load your managed DLL in that and then unload the AppDomain.
AppDomain ad = AppDomain.CreateDomain("Isolate DLL");
Assembly a = ad.Load(new AssemblyName("MyManagedDll"));
object d = a.CreateInstance("MyManagedDll.MyManagedClass");
Type t = d.GetType();
double result = (double)t.InvokeMember("Calculate", BindingFlags.InvokeMethod, null, d, new object[] { 1.0, 2.0 });
AppDomain.Unload(ad);
Here is what the DLL code looks like...
namespace MyManagedDll
{
public class MyManagedClass
{
public double Calculate(double a, double b)
{
return a + b;
}
}
}
I need to un wrap an Oracle Package created by some other dev.
I have the Prackage created in my DB, but in encrypted format.
The reason i need is, original Developer has left the organization and now the procedure define in the package needs to be redefine with updated changes in DB Structure and logic.
Can some one help me as How can i un wrap the package in oracle.
You can paste the code here and it will unwrap it for you.
Be advised you will lose all comments but variable names will remain.
But for fun lets test the logic.
First create the procedure:
sqlplus testing/testtest
SQL*Plus: Release 11.2.0.3.0 Production on Fri Oct 10 08:36:06 2014
Copyright (c) 1982, 2011, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> create or replace procedure AA as
2 begin
3 null;
4 /*comments*/
5 end;
6 /
Procedure created.
Next we will save the procedure into the OS:
SQL> save aa.sql
Created file aa.sql
SQL> exit
Disconnected from Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
oracle#HOSTNAME:/home/oracle/USER/wrapTest> ll
total 12K
drwxr-x---. 4 oracle oinstall 4.0K Oct 10 08:36 ../
-rw-r-----. 1 oracle oinstall 66 Oct 10 08:37 aa.sql
drwxr-x---. 2 oracle oinstall 4.0K Oct 10 08:37 ./
After saving it we will use the seeded wrap utility to obfuscate the package:
oracle#HOSTNAME:/home/oracle/USER/wrapTest> wrap iname=aa.sql oname=aa.pls
PL/SQL Wrapper: Release 11.2.0.3.0- 64bit Production on Fri Oct 10 08:37:29 2014
Copyright (c) 1993, 2009, Oracle. All rights reserved.
Processing aa.sql to aa.pls
Now lets see what it looks like:
oracle#HOSTNAME:/home/oracle/USER/wrapTest> cat aa.
aa.pls aa.sql
oracle#HOSTNAME:/home/oracle/USER/wrapTest> cat aa.pls
create or replace procedure AA wrapped
a000000
1f
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
7
22 55
7weeW1mRAdYVG9cX0WEujCaQghIwg5nnm7+fMr2ywFy49cO4dIvAwDL+0oabmYEILYsGwIHH
LcmmpnWE55Q=
/
So we copy that code into that link and this is what it looks like:
As you can see we lost the comments but retrieved the code.
All wrapping is, is a base64 encoded Caesar-ciphered compressed string. So if you don't want to paste your code into a website:
import javax.xml.bind.DatatypeConverter;
import java.util.zip.InflaterInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.FileReader;
public class Unwrap {
static public void main(String[] args) throws Exception {
byte[] charmap = new byte[] {
(byte)0X3d, (byte)0X65, (byte)0X85, (byte)0Xb3, (byte)0X18, (byte)0Xdb, (byte)0Xe2, (byte)0X87, (byte)0Xf1, (byte)0X52, (byte)0Xab, (byte)0X63, (byte)0X4b, (byte)0Xb5, (byte)0Xa0, (byte)0X5f, (byte)0X7d, (byte)0X68, (byte)0X7b, (byte)0X9b, (byte)0X24, (byte)0Xc2, (byte)0X28, (byte)0X67, (byte)0X8a, (byte)0Xde, (byte)0Xa4, (byte)0X26, (byte)0X1e, (byte)0X03, (byte)0Xeb, (byte)0X17
, (byte)0X6f, (byte)0X34, (byte)0X3e, (byte)0X7a, (byte)0X3f, (byte)0Xd2, (byte)0Xa9, (byte)0X6a, (byte)0X0f, (byte)0Xe9, (byte)0X35, (byte)0X56, (byte)0X1f, (byte)0Xb1, (byte)0X4d, (byte)0X10, (byte)0X78, (byte)0Xd9, (byte)0X75, (byte)0Xf6, (byte)0Xbc, (byte)0X41, (byte)0X04, (byte)0X81, (byte)0X61, (byte)0X06, (byte)0Xf9, (byte)0Xad, (byte)0Xd6, (byte)0Xd5, (byte)0X29, (byte)0X7e
, (byte)0X86, (byte)0X9e, (byte)0X79, (byte)0Xe5, (byte)0X05, (byte)0Xba, (byte)0X84, (byte)0Xcc, (byte)0X6e, (byte)0X27, (byte)0X8e, (byte)0Xb0, (byte)0X5d, (byte)0Xa8, (byte)0Xf3, (byte)0X9f, (byte)0Xd0, (byte)0Xa2, (byte)0X71, (byte)0Xb8, (byte)0X58, (byte)0Xdd, (byte)0X2c, (byte)0X38, (byte)0X99, (byte)0X4c, (byte)0X48, (byte)0X07, (byte)0X55, (byte)0Xe4, (byte)0X53, (byte)0X8c
, (byte)0X46, (byte)0Xb6, (byte)0X2d, (byte)0Xa5, (byte)0Xaf, (byte)0X32, (byte)0X22, (byte)0X40, (byte)0Xdc, (byte)0X50, (byte)0Xc3, (byte)0Xa1, (byte)0X25, (byte)0X8b, (byte)0X9c, (byte)0X16, (byte)0X60, (byte)0X5c, (byte)0Xcf, (byte)0Xfd, (byte)0X0c, (byte)0X98, (byte)0X1c, (byte)0Xd4, (byte)0X37, (byte)0X6d, (byte)0X3c, (byte)0X3a, (byte)0X30, (byte)0Xe8, (byte)0X6c, (byte)0X31
, (byte)0X47, (byte)0Xf5, (byte)0X33, (byte)0Xda, (byte)0X43, (byte)0Xc8, (byte)0Xe3, (byte)0X5e, (byte)0X19, (byte)0X94, (byte)0Xec, (byte)0Xe6, (byte)0Xa3, (byte)0X95, (byte)0X14, (byte)0Xe0, (byte)0X9d, (byte)0X64, (byte)0Xfa, (byte)0X59, (byte)0X15, (byte)0Xc5, (byte)0X2f, (byte)0Xca, (byte)0Xbb, (byte)0X0b, (byte)0Xdf, (byte)0Xf2, (byte)0X97, (byte)0Xbf, (byte)0X0a, (byte)0X76
, (byte)0Xb4, (byte)0X49, (byte)0X44, (byte)0X5a, (byte)0X1d, (byte)0Xf0, (byte)0X00, (byte)0X96, (byte)0X21, (byte)0X80, (byte)0X7f, (byte)0X1a, (byte)0X82, (byte)0X39, (byte)0X4f, (byte)0Xc1, (byte)0Xa7, (byte)0Xd7, (byte)0X0d, (byte)0Xd1, (byte)0Xd8, (byte)0Xff, (byte)0X13, (byte)0X93, (byte)0X70, (byte)0Xee, (byte)0X5b, (byte)0Xef, (byte)0Xbe, (byte)0X09, (byte)0Xb9, (byte)0X77
, (byte)0X72, (byte)0Xe7, (byte)0Xb2, (byte)0X54, (byte)0Xb7, (byte)0X2a, (byte)0Xc7, (byte)0X73, (byte)0X90, (byte)0X66, (byte)0X20, (byte)0X0e, (byte)0X51, (byte)0Xed, (byte)0Xf8, (byte)0X7c, (byte)0X8f, (byte)0X2e, (byte)0Xf4, (byte)0X12, (byte)0Xc6, (byte)0X2b, (byte)0X83, (byte)0Xcd, (byte)0Xac, (byte)0Xcb, (byte)0X3b, (byte)0Xc4, (byte)0X4e, (byte)0Xc0, (byte)0X69, (byte)0X36
, (byte)0X62, (byte)0X02, (byte)0Xae, (byte)0X88, (byte)0Xfc, (byte)0Xaa, (byte)0X42, (byte)0X08, (byte)0Xa6, (byte)0X45, (byte)0X57, (byte)0Xd3, (byte)0X9a, (byte)0Xbd, (byte)0Xe1, (byte)0X23, (byte)0X8d, (byte)0X92, (byte)0X4a, (byte)0X11, (byte)0X89, (byte)0X74, (byte)0X6b, (byte)0X91, (byte)0Xfb, (byte)0Xfe, (byte)0Xc9, (byte)0X01, (byte)0Xea, (byte)0X1b, (byte)0Xf7, (byte)0Xce };
String line;
BufferedReader br = new BufferedReader(new FileReader(args[0]));
int l = 0;
String s = "";
while ((line = br.readLine()) != null) {
if (l>0) {
l -= line.length()+1;
s += line;
} else if (l<0) {
l = 0;
byte[] b = DatatypeConverter.parseBase64Binary(s);
byte[] c = new byte[b.length-20];
for (int i = 20; i < b.length; i++)
c[i-20] = (byte)(charmap[b[i]&255]);
InputStream is = new InflaterInputStream(new ByteArrayInputStream(c));
byte[] buffer = new byte[1000];
int len;
while((len = is.read(buffer)) > 0)
System.out.write(buffer, 0, len);
}
if (line.matches("^[0-9a-f]+ ([0-9a-f]+)$")) {
l = Integer.parseInt(line.substring(1+line.lastIndexOf(' ')),16);
s = "";
}
}
}
}
If you are using SQL Developer, you can install an add-in PL/SQL Unwrapper for SQL Developer from Salvis to unwrap the proc. ref. https://github.com/Trivadis/plsql-unwrapper-sqldev
Step by Step:
Open SQL Developer
Help > Check for Updates, click Add
Set
Name=Salvis
Location=http://update.salvis.com/
then OK, then check Salvis, then Next
Let it add and update and restart SQL Developer
After restart, find a procedure that is wrapped and open it in the editor
Right Click the editor and new option "Unwrap" is presented, click it and voila your proc should be plain text.
We were struggling with a situation where an Oracle ASP.NET Membership provider proc ora_aspnet_PPU_SetPgSettings was not working like its SQL Server counterpart. After unwrapping it, we discovered a bug there and we were able to fix.
Try this page: http://www.codecrete.net/UnwrapIt/
There you can simply paste your code.