We have currently shipped with our ERP a simple utility that installs the Firebird Server 2.5 and the Firebird ODBC Driver programmatically.
The server I install by executing some .bat files from the utility and it works perfectly on the port 3060.
The ODBC I install by adding the odbc keys to the registry and then copy gds32.dll and odbcfb.dll files to the system32 folder.
We have been using this utility for more than a year now and the client instalation works ok, but sometimes there is an older version of the gds32.dll on the system32 folder being used by an older version of Firebird Server or an Interbase server and I can't simply update the gds32.dll because it would stop any other software that is using it.
Here's the driver instalation code:
class OdbcDriverHelper
{
private static string ODBCINST_INI_REG_PATH = "SOFTWARE\\ODBC\\ODBCINST.INI\\";
private static string ODBCINST_INI_REG_PATH_DRIVERS = "SOFTWARE\\ODBC\\ODBCINST.INI\\ODBC Drivers\\";
private static string ODBCINST_INI_REG_PATH_6432 = "SOFTWARE\\Wow6432Node\\ODBC\\ODBCINST.INI\\";
public static void AddEntry(string nomeDriver, string caminhoDriver)
{
var driverKey = Registry.LocalMachine.CreateSubKey(ODBCINST_INI_REG_PATH + nomeDriver);
if (driverKey == null) throw new Exception(string.Format("ODBC Registry key for driver '{0}' does not exist", nomeDriver));
driverKey.SetValue("APILevel", 1);
driverKey.SetValue("ConnectFunctions", "YYY");
driverKey.SetValue("Driver", caminhoDriver);
driverKey.SetValue("DriverODBCVer", 03.51);
driverKey.SetValue("FileExtns", "*.fdb,*.gdb");
driverKey.SetValue("FileUsage", 0);
driverKey.SetValue("Setup", caminhoDriver);
driverKey.SetValue("SQLLevel", 1);
driverKey.SetValue("UsageCount", 0x00000001);
if (SystemInfo.is64BitOperatingSystem)
{
driverKey = Registry.LocalMachine.CreateSubKey(ODBCINST_INI_REG_PATH_6432 + nomeDriver);
if (driverKey == null) throw new Exception(string.Format("ODBC Registry key for driver on WIN6432NODE '{0}' does not exist", nomeDriver));
driverKey.SetValue("APILevel", 1);
driverKey.SetValue("ConnectFunctions", "YYY");
driverKey.SetValue("Driver", caminhoDriver);
driverKey.SetValue("DriverODBCVer", 03.51);
driverKey.SetValue("FileExtns", "*.fdb,*.gdb");
driverKey.SetValue("FileUsage", 0);
driverKey.SetValue("Setup", caminhoDriver);
driverKey.SetValue("SQLLevel", 1);
driverKey.SetValue("UsageCount", 0x00000001);
}
}
public static void InstallDriver()
{
if (!File.Exists(Constants.CAM_DRIVER_32))
File.Copy("Firebird_32\\odbc\\OdbcFb.dll", Constants.CAM_DRIVER_32, true);
if (!File.Exists(Constants.CAM_GDS32_32))
File.Copy("Firebird_32\\odbc\\GDS32.dll", Constants.CAM_GDS32_32, true);
AddEntry(Constants.ODBC_DRIVER_NAME, Constants.CAM_DRIVER_32);
}
}
Note: This is not the original code, there are some more variables that I switched to hardcoded strings and a few try catch fallbacks that I didn't include just to keep it more clean.
Is there any other proper way of doing this without having to worry about these types of conflicts how would you do this programmatic installation more efficiently?
Related
I need to develop a Shell Context Menu extension that references some other custom assemblies... I don't want to assign a Strong Name Key to those custom assemblies!
The guide I followed to do this uses the SharpShell project and illustrates how to sign (but does not expalins why) the assembly... and this is my problem: if I sign my final .dll then I have many errors during my project's building phase, because some assemblies my project references are not strongly named ("Referenced assembly does not have a strong name").
In general, googling about the C# Shell Extension implementation, all best tutorials I found sign the final assembly... is it mandatory?
Without signing the assembly ServerManager.exe returns this error: "The file 'XYZ.dll' is not a SharpShell Server".
Finally I've solved my troubles... the SharpShell.dll file obtained through NuGet was a different version of the ServerManager.exe ones.
Uninstalling the SharpShell NuGet package and directly referencing the SharpShell.dll you find inside the ServerManager folder was my solution!
Moreover, I was looking between the article comments... please read this question.
You don't need to use old DLL.
Please use this code directly, without using ServerManager.exe.
private static ServerEntry serverEntry = null;
public static ServerEntry SelectedServerEntry
{
get
{
if (serverEntry == null)
serverEntry = ServerManagerApi.LoadServer("xxx.dll");
return serverEntry;
}
}
public static ServerEntry LoadServer(string path)
{
try
{
// Create a server entry for the server.
var serverEntry = new ServerEntry();
// Set the data.
serverEntry.ServerName = Path.GetFileNameWithoutExtension(path);
serverEntry.ServerPath = path;
// Create an assembly catalog for the assembly and a container from it.
var catalog = new AssemblyCatalog(Path.GetFullPath(path));
var container = new CompositionContainer(catalog);
// Get the exported server.
var server = container.GetExport<ISharpShellServer>().Value;
serverEntry.ServerType = server.ServerType;
serverEntry.ClassId = server.GetType().GUID;
serverEntry.Server = server;
return serverEntry;
}
catch (Exception)
{
// It's almost certainly not a COM server.
MessageBox.Show("The file '" + Path.GetFileName(path) + "' is not a SharpShell Server.", "Warning");
return null;
}
}
Install code:
ServerRegistrationManager.InstallServer(SelectedServerEntry.Server, RegistrationType.OS64Bit, true);
Register code:
ServerRegistrationManager.RegisterServer(SelectedServerEntry.Server, RegistrationType.OS64Bit);
With the following code:
static void Main()
{
try
{
var context = uno.util.Bootstrap.bootstrap();
}
catch (Exception ex)
{
Console.WriteLine(ex.toString());
}
}
I can start Writer of LibreOffice. This works fine with Version 4.4.4 but after installing version 5.0.0 and with new SDK Bootstrap.bootstrap() throws the exception:
"External component has thrown an exception"
Has anyone faced the same problem or some solution?
(.NET 4.0, Windows 7 64-bit, LibreOffice 5.0 Lite)
I have managed to solve the problem by setting the UNO_PATH environment variable before starting the soffice.exe server:
using static System.Environment;
var unoPath = #"C:\Program Files\LibreOffice 5\program"
// when running 32-bit LibreOffice on a 64-bit system, the path will be in Program Files (x86)
// var unoPath = #"C:\Program Files (x86)\LibreOffice 5\program"
SetEnvironmentVariable("UNO_PATH", unoPath, EnvironmentVariableTarget.Process);
SetEnvironmentVariable("PATH", GetEnvironmentVariable("PATH") + #";" + unoPath, EnvironmentVariableTarget.Process);
This was required because LibreOffice 5's program directory does not have "URE" subdirectory anymore (previous versions did) which is required for UNO layer.
To get the path to the LibreOffice installation you can ask e.g. the Windows registry. In C# this is smoething like that:
String unoPath = "";
// access 32bit registry entry for latest LibreOffice for Current User
Microsoft.Win32.RegistryKey hkcuView32 = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.CurrentUser, Microsoft.Win32.RegistryView.Registry32);
Microsoft.Win32.RegistryKey hkcuUnoInstallPathKey = hkcuView32.OpenSubKey(#"SOFTWARE\LibreOffice\UNO\InstallPath", false);
if (hkcuUnoInstallPathKey != null && hkcuUnoInstallPathKey.ValueCount > 0)
{
unoPath = (string)hkcuUnoInstallPathKey.GetValue(hkcuUnoInstallPathKey.GetValueNames()[hkcuUnoInstallPathKey.ValueCount - 1]);
}
else
{
// access 32bit registry entry for latest LibreOffice for Local Machine (All Users)
Microsoft.Win32.RegistryKey hklmView32 = Microsoft.Win32.RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, Microsoft.Win32.RegistryView.Registry32);
Microsoft.Win32.RegistryKey hklmUnoInstallPathKey = hklmView32.OpenSubKey(#"SOFTWARE\LibreOffice\UNO\InstallPath", false);
if (hklmUnoInstallPathKey != null && hklmUnoInstallPathKey.ValueCount > 0)
{
unoPath = (string)hklmUnoInstallPathKey.GetValue(hklmUnoInstallPathKey.GetValueNames()[hklmUnoInstallPathKey.ValueCount - 1]);
}
}
Then you can use the answer of Funbit [ https://stackoverflow.com/a/31937114/2936206 ]
The most easiest way I found is just copy the URE folder from previous LibreOffice version to LibreOffice 5.
As mentioned here wix-bootstrapper-update-ui-xaml-from-customaction I use a Bootstrapper to install two MSI-packages.
During the installation I want to install/update a Visual FoxPro database (consisting of free tables).
At the moment I achieve this by calling a Visual FoxPro-exe during the ApplyComplete-Event of the BootstrapperApplication. To establish a communication between the BootstrapperApplication and the Visual FoxPro-exe I use MSMQ :
private void OnApplyComplete(object sender, ApplyCompleteEventArgs e)
{
string updateFile = this.installationFolder + "\\updfile.exe";
if (!MessageQueue.Exists(NAMEDPVDATENBANKNACHRICHTENSCHLANGE))
{
this._msmq = MessageQueue.Create(UPDATEMSMQ);
}
else
{
this._msmq = new MessageQueue(UPDATEMSMQ);
}
this._msmq.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
this._msmq.Purge();
if (System.IO.File.Exists(updateFile))
{
this._dbUpdate = true;
ProcessStartInfo updateProcessInfo = new ProcessStartInfo();
updateProcessInfo.FileName = updateFile;
updateProcessInfo.Arguments = UPDATEMSMQ;
updateProcessInfo.UseShellExecute = true;
updateProcessInfo.WorkingDirectory = this.installationFolder;
updateProcessInfo.CreateNoWindow = true;
Process updateProcess = new Process();
updateProcess.StartInfo = updateProcessInfo;
updateProcess.EnableRaisingEvents = true;
updateProcess.Exited += new EventHandler(this.updateFinished);
updateProcess .Start();
while (this._dbUpdate)
{
Message msg = null;
try
{
nachricht = this._msmq.Receive(new TimeSpan(0, 0, 45));
}
catch (MessageQueueException msgEx)
{
if (nachrichtAusnahme.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
{
this.Engine.Log(LogLevel.Verbose, msgEx);
}
}
if (msg != null)
{
msg.Formatter = new ActiveXMessageFormatter();
this.Engine.Log(LogLevel.Verbose, "VfpUpdate - " + msg.Body.ToString());
}
}
}
this._msmq.Close();
MessageQueue.Delete(UPDATEMSMQ);
}
private void updateFinished(object sender, EventArgs e)
{
this._dbUpdate = false;
this.Engine.Log(LogLevel.Verbose, "Update finished");
}
This way it works like a charm unless there are errors during the the update of the Visual FoxPro database. It should be possible to roll-back the changes made during the installation. For me it would be no problem to create a backup of the Visual FoxPro-files and to restore the files if an error occurs. But how should I do this with the files changed by the actual Bootstrapper?
With a CustomAction I can use ActionResult.Failure or ActionResult.Success. But with a CustomAction I face the following issues:
no access to Application.Current (for reading values from a customized ResourceDictionary with localized strings that I use within the Bootstrapper)
MSMQ-queue is broken (closed?!) after the first message is delivered
display the currently performed task in the MainWindow.
Any advice on how to perform the update of the Visual FoxPro database inside a BootstrapperApplication is really welcome.
I don't know that in the context of an installation bootstrapper. OTOH, either a VFP database and tables or VFP tables (free) are just plain files that are copied/moved from a folder. You don't need to have an exe for that to create them programmatically.
Also having those files as part of the setup might not be a good idea as well. If some files are removed because of version changes then your installer might start to bark about that.
Looking at the bootstrapper code, I have a suspicion that you are creating those free tables in the installation folder or its subfolders which would be a real PITA. Because installation folder is generally under "Program Files (x86)" which is a read only location.
Your bootstrapper code is C#. You might use C# to create those tables as well. One way to do that is to use VFPOLEDB and ExecScript() calls.
I was trying to create send ports using C# .NET through following code :
using Microsoft.BizTalk.ExplorerOM;
private void CreateSendPort()
{
// connect to the local BizTalk Management database
BtsCatalogExplorer catalog = new BtsCatalogExplorer();
catalog.ConnectionString = "Server=.;Initial Catalog=BizTalkMgmtDb;Integrated Security=SSPI;";
try
{
// create a new static one-way SendPort
SendPort myStaticOnewaySendPort = catalog.AddNewSendPort(false, false);
myStaticOnewaySendPort.Name = "myStaticOnewaySendPort1";
myStaticOnewaySendPort.PrimaryTransport.TransportType = catalog.ProtocolTypes[0];
myStaticOnewaySendPort.PrimaryTransport.Address = "http://sample1";
myStaticOnewaySendPort.SendPipeline = catalog.Pipelines["Microsoft.BizTalk.DefaultPipelines.XMLTransmit"];
// create a new dynamic two-way sendPort
SendPort myDynamicTwowaySendPort = catalog.AddNewSendPort(true, true);
myDynamicTwowaySendPort.Name = "myDynamicTwowaySendPort1";
myDynamicTwowaySendPort.SendPipeline = catalog.Pipelines["Microsoft.BizTalk.DefaultPipelines.XMLTransmit"];
myDynamicTwowaySendPort.ReceivePipeline = catalog.Pipelines["Microsoft.BizTalk.DefaultPipelines.XMLReceive"];
// persist changes to BizTalk Management database
catalog.SaveChanges();
}
catch(Exception e)
{
catalog.DiscardChanges();
throw e;
}
}
Source
But I'm getting following issue
Explorer OM is not supported in a 64bit process.
when this line is executed :
BtsCatalogExplorer catalog = new BtsCatalogExplorer();
I'm well aware of the fact i.e. : "Warning
Microsoft.BizTalk.ExplorerOM.dll is only supported if used from 32 bit processes. If you are building a solution for a 64 bit system you should not use this library."
But in this case how can I create send ports on 64bit machine, Can anybody please help me with this?
Force it to run in a 32 bit process.
http://lostechies.com/gabrielschenker/2009/10/21/force-net-application-to-run-in-32bit-process-on-64bit-os/
As of BizTalk 2010, this restriction was lifted and ExplorerOM can be used in 64-bit and 32-bit processes.
I want to check whether certain microsoft components like wmencoder, directx or wmplayer
are installed or not. If it is installed, can I also get its version number?
How can I do that?
Thanks in advance.
I use the below to determine if other applications are installed, however you will need to know the "unique" product code (from the setup project in Visual Studio) that the application is installed with in the registry.
Include
using System.Diagnostics;
using Microsoft.Win32;
Usage:
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0006F03A-0000-0000-C000-000000000046} << This is outlook 2003
String retval = "";
// Look to see if Outlook 2003 is installed and if it is...
if ((checkComServerExists("{0006F03A-0000-0000-C000-000000000046}", out retval)))
{
// Update boolean flag if we get this far so we don't have to check again
Console.WriteLine("Office CSLID exists - Version: " + retval);
}
Function:
// Checks to see if the given CLSID is registerd and exists on the system
private static Boolean checkComServerExists(String CLSID, out String retval)
{
RegistryKey myRegKey = Registry.LocalMachine;
Object val;
try
{
// get the pathname to the COM server DLL/EXE if the key exists
myRegKey = myRegKey.OpenSubKey("SOFTWARE\\Classes\\CLSID\\" + CLSID + "\\LocalServer32");
val = myRegKey.GetValue(null); // the null gets default
}
catch
{
retval = "CLSID not registered";
return false;
}
FileVersionInfo myFileVersionInfo = null;
try
{
// parse out the version number embedded in the resource
// in the DLL
myFileVersionInfo = FileVersionInfo.GetVersionInfo(val.ToString());
}
catch
{
retval = String.Format("DLL {0} not found", val.ToString());
return false;
}
retval = myFileVersionInfo.FileVersion;
return true;
}
My first thought would be WMI. Class Win32_SoftwareElement (on MSDN)
But likely to take some work to get the right classes and queries. Start with the WMI tools for WMI CIM Studio.
Using PowerShell, something like:
gwmi win32_softwareelement -filter "name like '%play%'" | ft
will allow finding the right ids. (Warning: this is extremely slow.)
Possible that the MS Installer (MSI) API has something quicker.
I use RegShot to determine registry setting that can be used to check if a softwre is installed ..
Here is also a small code snippet that uses, among others, Type.GetTypeFromProgID and registry access.