Using C# How to detect if Windows Installer 4.5 is Installed - c#

I am trying to figure out the most efficient way to determine if Windows Installer 4.5 is installed on a machine.
I have a 2.0 application (cannot convert at this time to 3.5) and we are upgrading from MSDE to SQL 2008 Express. One of the requirements of 2008 Express is that Windows Installer 4.5 is installed on the machine. This application is deployed globally to machines both on and off of an internal network.
I would prefer to run a batch file or C# code to determine the installer version.
Please let me know your recommended methods and provide some code (or links to code).
Thanks!

You can read the file version of the msi.dll library in the system directory:
using System.Diagnostics;
using System.IO;
public bool IsWindowsInstaller45Installed()
{
FileVersionInfo info;
string fileName = Path.Combine(Environment.SystemDirectory, "msi.dll");
try {
info = FileVersionInfo.GetVersionInfo(fileName);
} catch (FileNotFoundException) {
return false;
}
return (info.FileMajorPart > 4
|| info.FileMajorPart == 4 && info.FileMinorPart >= 5);
}

Check the version of the MSI.DLL file that's in your System32 directory.
You should be able to use GetFileVersionInfo or GetFileVersionInfoEx to get out the version number.
This MSDN article has some sample code: Unsafe Code Tutorial

Like Ho1 said, you can go by the version of MSI.dll in System32 but you don't need to P/Invoke you can use the FileVersionInfo class found in System.Diagnostics.

Related

C# + LibreOffice SDK

I am developing a desktop application in .NET (C# + WPF) and my machine works correctly, I can make the LibreOffice call, open a file and customize the interface all through my .NET application, but in order to work, it is necessary the version of the SDK is the same as that of LibreOffice installed on the machine, and the architecture of the application should be the same as that of LibreOffice (32 or 64 bits).
To work the integration, I had to add in the references of my project the DLLs that comes along with the SDK:
cli_basetypes.dll
cli_cppuhelper.dll
cli_oootypes.dll
cli_ure.dll
cli_uretypes.dll
So at first all right, but my question is this: I developed the application using LibreOffice 6.1 along with the SDK of the same version and now I need the application to run on another machine with a lower version of LibreOffice, which I can not currently get, with the following error occurring:
System.IO.FileNotFoundException: Could not load file or assembly 'cli_cppuhelper.dll' or one of its dependencies. The specified module could not be found.
Is it possible to run the application on another machine with a different version of LibreOffice? How? Also, is it possible to avoid errors because the application is developed in 64bit and LibreOffice installed is 32bit for example?
After many attempts I was able to solve the problem.
It was necessary to override the AssemblyResolve event to fetch the LibreOffice DLLs from the GAC (C:\Windows\Microsoft.NET\assembly).
In addition the application must run with the same LibreOffice architecture installed so it was necessary to generate two executables, one 32bits and another 64bits (checking/unchecking the "32-bit Preferred" project property) so that the AssemblyResolve event finds the right DLL in GAC.
Another necessary change was necessary to set in the environment variable "UNO_PATH" that can be found in the Windows registry;
string unoPath = "";
RegistryKey hkcuView32 = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.CurrentUser, RegistryView.Default);
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
{
RegistryKey hklmView32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default);
RegistryKey hklmUnoInstallPathKey = hklmView32.OpenSubKey(#"SOFTWARE\LibreOffice\UNO\InstallPath", false);
if (hklmUnoInstallPathKey != null && hklmUnoInstallPathKey.ValueCount > 0)
{
unoPath = (string)hklmUnoInstallPathKey.GetValue(hklmUnoInstallPathKey.GetValueNames()[hklmUnoInstallPathKey.ValueCount - 1]);
}
}
Environment.SetEnvironmentVariable("UNO_PATH", unoPath, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") + #";" + unoPath, EnvironmentVariableTarget.Process);
After this steps, my application worked perfectly!

GhostscriptLibraryNotInstalledException running under 32-bit process requires native library

Using nuget in Visual Studio 2013, I installed Ghostscript.NET into my project on my Windows x64 PC.
Just to make sure I wasn't crazy, I checked it:
PM> Install-Package Ghostscript.NET
'Ghostscript.NET 1.2.0' already installed.
Project already has a reference to 'Ghostscript.NET 1.2.0'.
PM>
The project is used by multiple developers. It targets Any CPU, and needs to remain that way.
Here is my code:
public static void GhostscriptNetProcess(String fileName, String outputPath)
{
var version = GhostscriptVersionInfo.GetLastInstalledVersion();
var source = (fileName.IndexOf(' ') == -1) ? fileName : String.Format("\"{0}\"", fileName);
var output_file = (outputPath.IndexOf(' ') == -1) ? outputPath : String.Format("\"{0}\"", outputPath);
var gsArgs = new List<String>();
gsArgs.Add("-q");
gsArgs.Add("-dNOPAUSE");
gsArgs.Add("-dNOPROMPT");
gsArgs.Add("-sDEVICE=pdfwrite");
gsArgs.Add(String.Format(#"-sOutputFile={0}", output_file));
gsArgs.Add("-f");
gsArgs.Add(source);
var processor = new GhostscriptProcessor(version, false);
processor.Process(gsArgs.ToArray());
}
Whenever I attempt to debug the application, I get the following error message:
GhostscriptLibraryNotInstalledException was unhandled
An unhandled exception of type 'Ghostscript.NET.GhostscriptLibraryNotInstalledException' occurred in Ghostscript.NET.dll
Additional information: This managed library is running under 32-bit process and requires 32-bit Ghostscript native library installation on this machine! To download proper Ghostscript native library please visit: http://www.ghostscript.com/download/gsdnld.html
Looking up the Ghostscript.NET.GhostscriptLibraryNotInstalledException did not provide any useful information, though this post on CodeProject indicated that the debugger is running in 32-bit mode whereas I have the 64-bit version installed.
That's all well and good know, but how do I go about testing the new code I wrote that uses Ghostscript?
If you are testing with MS Test you have to set the processor architecture in which the tests are run, because Ghostscript.Net verifies the process architecture (Environment.Is64BitProcess) to search for the ghostscript installation in the registry.
In Menu > Test > Test Settings > Default Processor Architecture > X64.
Have you actually installed Ghostscript ?
Ghostscript.NET is merely a .NET interface to Ghostscript, it looks to me like the message:
"This managed library is running under 32-bit process and requires 32-bit Ghostscript native library installation on this machine! To download proper Ghostscript native library please visit: http://www.ghostscript.com/download/gsdnld.html"
is trying to tell you that you don;t have a 32-bit version of Ghostscript installed. It even tells you where to go to download a copy.
So have you installed Ghostscript ? Have you installed the 32-bit version of Ghostscript ?

C# & Windows Update Api (WUApiLib)

I am using Windows Update API (WUApiLib) in a C# .NET 2.0 project.
I get the following error on Windows XP (in Windows 7 it works alright):
"System.MissingMethodException: Method not found: 'WUApiLib.UpdateSearcher WUApiLib.UpdateSessionClass.CreateUpdateSearcher()'."
This is my code:
WUApiLib.UpdateSessionClass session = new WUApiLib.UpdateSessionClass();
WUApiLib.IUpdateSearcher searcher = session.CreateUpdateSearcher();
WUApiLib.ISearchResult result = searcher.Search("Type='Software'");
if (result.Updates.Count > 0)
{
//do stuff
}
The error occurs at runtime, the compiler shows no errors...
Does anybody know why I get this error?
You are no doubt battling a version problem, the Win7 version is WUA version 2, XP is probably still stuck at 1. If you want to support XP then make sure to use an interop library that you generated with Tlbimp.exe on a XP machine. Keeps you out of trouble, you can't accidentally use an interface or method that isn't supported on XP.
I don't see much in the way of documentation for what's available in which version and to what degree version 2 is backwards compatible with version 1. Only this MSDN Library page seems relevant.

Getting the path of the home directory in C#?

Okay, I've checked Environment.SpecialFolder, but there's nothing in there for this.
I want to get the home directory of the current user in C#. (e.g. c:\documents and settings\user under XP, c:\users\user under Vista, and /home/user under Unix.)
I know I can read enviroment variables to find this out, but I want to do this in a cross-platform way.
Is there any way I can do this with .NET (preferably using mscorlib)?
UPDATE: Okay, this is the code I ended up using:
string homePath = (Environment.OSVersion.Platform == PlatformID.Unix ||
Environment.OSVersion.Platform == PlatformID.MacOSX)
? Environment.GetEnvironmentVariable("HOME")
: Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%");
You are looking for Environment.SpecialFolder.UserProfile which refers to C:\Users\myname on Windows and /home/myname on Unix/Linux:
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
Note that Environment.SpecialFolder.Personal is My Documents (or Documents in win7 and above), but same as home directory on Unix/Linux.
Environment.SpecialFolder.Personal doesn't actually return the home folder, it returns the My Documents folder. The safest way to get the home folder on Win32 is to read %HOMEDRIVE%%HOMEPATH%. Reading environment variables is actually very portable to do (across Unix and Windows), so I'm not sure why the poster wanted to not do it.
Edited to add: For crossplatform (Windows/Unix) C#, I'd read $HOME on Unix and OSX and %HOMEDRIVE%%HOMEPATH% on Windows.
I believe what you are looking for is:
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)
For reference, it is infact contained in mscorlib.
In DotNetCore 1.1 System.Environment.SpecialFolder does not exist. It might exist in 2.0-beta. Until then, to do this you can use the following:
var envHome = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "HOMEPATH" : "HOME";
var home = Environment.GetEnvironmentVariable(envHome);`
The bottom line answer is No. The is no simple System based method in .NET to get the Home directory such that we could expect an implementation in both .NET on Windows and in Mono.
You will need to do some OS detection and branch to OS specific code.
When you say cross-platform way, what other OSs do you need to support? You may need to do some simple OS detection to select the method for finding the home directory if you're running on a non-Windows OS.
This website seems to give a way to do what you need in Windows.
This can be done using GetEnvironmentVariable in System.IO:
public string GetUserHome() {
var homeDrive = Environment.GetEnvironmentVariable("HOMEDRIVE");
if (!string.IsNullOrWhiteSpace(homeDrive))
{
var homePath = Environment.GetEnvironmentVariable("HOMEPATH");
if (!string.IsNullOrWhiteSpace(homePath))
{
var fullHomePath = homeDrive + Path.DirectorySeparatorChar + homePath;
return Path.Combine(fullHomePath, "myFolder");
}
else
{
throw new Exception("Environment variable error, there is no 'HOMEPATH'");
}
}
else
{
throw new Exception("Environment variable error, there is no 'HOMEDRIVE'");
}
}
Then it produces under windows: C:\\\\Users\\myusername\\myFolder
Note that if you use
var fullHomePath = Path.Combine(homeDrive.ToString(), homePath.ToString())
it fails because it produces: \\Users\\myusername\\myFolder
I don't have a machine to test it on, but %HOMESHARE% might work for you. Otherwise, here's a pretty good list of environment variables.
Just for future reference, the list of pre-defined variables in Windows 10, taken from Windows 10 default environment variables.
VARIABLE
WINDOWS 10
%ALLUSERSPROFILE%
C:\ProgramData
%APPDATA%
C:\Users{username}\AppData\Roaming
%CD%
Current directory full path.(cmd only)
%CMDCMDLINE%
Returns exact command line used to start current cmd.exe session.(cmd only)
%CMDEXTVERSION%
Number of current command processor extensions.(cmd only)
%CommonProgramFiles%
C:\Program Files\Common Files
%CommonProgramFiles(x86)%
C:\Program Files (x86)\Common Files
%CommonProgramW6432%
C:\Program Files\Common Files
%COMPUTERNAME%
The computer name of the current local system.
%COMSPEC%
C:\Windows\System32\cmd.exe
%DATE%
Current date in format determined by Date command.(cmd only)
%ERRORLEVEL%
Number defining exit status of previous command or program.(cmd only)
%HOMEDRIVE%
C:|
%HOMEPATH%
C:\Users{username}
%LOCALAPPDATA%
C:\Users{username}\AppData\Local
%LOGONSERVER%
\{domain_logon_server}\MicrosoftAccount
%NUMBER_OF_PROCESSORS%
8
%OS%
Windows_NT
%PATH%
C:\Windows;C:\Windows\System32;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0
%PathExt%
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
%PROCESSOR_ARCHITECTURE%
AMD64
%PROCESSOR_IDENTIFIER%
Intel64 Family 6 Model 158 Stepping 9, GenuineIntel
%PROCESSOR_LEVEL%
6
%PROCESSOR_REVISION%
9e09
%PROGRAMDATA%
C:\ProgramData
%PROGRAMFILES%
C:\Program Files
%ProgramW6432%
C:\Program Files
%PROGRAMFILES(X86)%
C:\Program Files (x86)
%PROMPT%
Code for current command prompt format.Code is usually $P$G(cmd only)
%PSModulePath%
C:\Windows\system32\WindowsPowerShell\v1.0\Modules|
%PUBLIC%
C:\Users\Public
%RANDOM%
To get random number between 0 and 32767.(cmd only)
%SessionName%
When logging on directly to machine, returns "Console".When client connects via terminal server session, is combinationof connection name, followed by pound symbol {#} and session number.
%SystemDrive%
C:
%SystemRoot%
C:\Windows
%TEMP%
C:\Users{username}\AppData\Local\Temp
%TMP%
C:\Users{username}\AppData\Local\Temp
%TIME%
Current time in format determined by Time command.(cmd only)
%USERDOMAIN%
The network domain name associated with the current user.
%USERDOMAIN_ROAMINGPROFILE%
The network domain name associated with the current roaming profile.
%USERNAME%
{username}
%USERPROFILE%
C:\Users{username}
%WINDIR%
C:\Windows

What is the minimum client footprint required to connect C# to an Oracle database?

I have successfully connected to an Oracle database (10g) from C# (Visual Studio 2008) by downloading and installing the client administration tools and Visual Studio 2008 on my laptop.
The installation footprint for Oracle Client tools was over 200Mb, and quite long winded.
Does anyone know what the minimum workable footprint is? I am hoping that it's a single DLL and a register command, but I have the feeling I need to install an oracle home, and set various environment variables.
I am using Oracle.DataAccess in my code.
You need an Oracle Client to connect to an Oracle database. The easiest way is to install the Oracle Data Access Components.
To minimize the footprint, I suggest the following :
Use the Microsoft provider for Oracle (System.Data.OracleClient), which ships with the framework.
Download the Oracle Instant Client Package - Basic Lite : this is a zip file with (almost) the bare minimum. I recommend version 10.2.0.4, which is much smaller than version 11.1.0.6.0.
Unzip the following files in a specific folder :
v10 :
oci.dll
orannzsbb10.dll
oraociicus10.dll
v11 :
oci.dll
orannzsbb11.dll
oraociei11.dll
On a x86 platform, add the CRT DLL for Visual Studio 2003 (msvcr71.dll) to this folder, as Oracle guys forgot to read this...
Add this folder to the PATH environment variable.
Use the Easy Connect Naming method in your application to get rid of the infamous TNSNAMES.ORA configuration file. It looks like this : sales-server:1521/sales.us.acme.com.
This amounts to about 19Mb (v10).
If you do not care about sharing this folder between several applications, an alternative would be to ship the above mentioned DLLs along with your application binaries, and skip the PATH setting step.
If you absolutely need to use the Oracle provider (Oracle.DataAccess), you will need :
ODP .NET 11.1.0.6.20 (the first version which allegedly works with Instant Client).
Instant Client 11.1.0.6.0, obviously.
Note that I haven't tested this latest configuration...
I use the method suggested by Pandicus above, on Windows XP, using ODAC 11.2.0.2.1. The steps are as follows:
Download the "ODAC 11.2 Release 3 (11.2.0.2.1) with Xcopy Deployment" package from oracle.com (53 MB), and extract the ZIP.
Collect the following DLLs: oci.dll (1 MB), oraociei11.dll (130 MB!), OraOps11w.dll (0.4 MB), Oracle.DataAccess.dll (1 MB). The remaining stuff can be deleted, and nothing have to be installed.
Add a reference to Oracle.DataAccess.dll, add using Oracle.DataAccess.Client; to your code and now you can use types like OracleConnection, OracleCommand and OracleDataReader to access an Oracle database. See the class documentation for details. There is no need to use the tnsnames.ora configuration file, only the connection string must be set properly.
The above 4 DLLs have to be deployed along with your executable.
As of 2014, the OPD.NET, Managed Driver is the smallest footprint.
Here is a code usage comparison to the non-managed versions that previous (outdated) answers suggested:
http://docs.oracle.com/cd/E51173_01/win.122/e17732/intro005.htm#ODPNT148
You will need to download these dlls and reference Oracle.ManagedDataAccess.dll in your project:
Download the ODP.NET, Managed Driver Xcopy version only
Here is a typical foot print you will need to package with your release:
Oracle.ManagedDataAccess.dll
Oracle.ManagedDataAccessDTC.dll
all together, a whopping 6.4 MB for .Net 4.0.
This way allows you to connect with ODP.net using 5 redistributable files from oracle:
Chris's blog entry: Using the new ODP.Net to access Oracle from C# with simple deployment
Edit: In case the blog every goes down, here is a brief summary...
oci.dll
Oracle.DataAccess.dll
oraociicus11.dll
OraOps11w.dll
orannzsbb11.dll
oraocci11.dll
ociw32.dll
make sure you get ALL those DLL's from the same ODP.Net / ODAC distribution to avoid version number conflicts, and put them all in the same folder as your EXE
DevArt http://www.devart.com/, formerly CoreLab (crlab.com) supplies a pure-C# Oracle client. That's a single dll, and it works fine.
Here is an update for Oracle 11.2.0.4.0. I had success with the following procedure on Windows 7 using System.Data.OracleClient.
1. Download Instant Client Package - Basic Lite: Windows 32-Bit or 64-Bit.
2. Copy the following files to a location in your system path:
32-Bit
1,036,288 2013-10-11 oci.dll
348,160 2013-10-11 ociw32.dll
1,290,240 2013-09-21 orannzsbb11.dll
562,688 2013-10-11 oraocci11.dll
36,286,464 2013-10-11 oraociicus11.dll
64-Bit
691,712 2013-10-09 oci.dll
482,304 2013-10-09 ociw32.dll
1,603,072 2013-09-10 orannzsbb11.dll
1,235,456 2013-10-09 oraocci11.dll
45,935,104 2013-10-09 oraociicus11.dll
3. Construct a connection string that omits the need for tnsnames.ora.
(See examples in the test program below.)
4. Run this minimal C# program to test your installation:
using System;
using System.Data;
using System.Data.OracleClient;
class TestOracleInstantClient
{
static public void Main(string[] args)
{
const string host = "yourhost.yourdomain.com";
const string serviceName = "yourservice.yourdomain.com";
const string userId = "foo";
const string password = "bar";
var conn = new OracleConnection();
// Construct a connection string using Method 1 or 2.
conn.ConnectionString =
GetConnectionStringMethod1(host, serviceName, userId, password);
try
{
conn.Open();
Console.WriteLine("Connection succeeded.");
// Do something with the connection.
conn.Close();
}
catch (Exception e)
{
Console.WriteLine("Connection failed: " + e.Message);
}
}
static private string GetConnectionStringMethod1(
string host,
string serviceName,
string userId,
string password
)
{
string format =
"SERVER=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" +
"(HOST={0})(PORT=1521))" +
"(CONNECT_DATA=(SERVER=DEDICATED)" +
"(SERVICE_NAME={1})));" +
"uid={2};" +
"pwd={3};"; // assumes port is 1521 (the default)
return String.Format(format, host, serviceName, userId, password);
}
static private string GetConnectionStringMethod2(
string host,
string serviceName,
string userId,
string password
)
{
string format =
"Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" +
"(HOST={0})(PORT=1521))" +
"(CONNECT_DATA=(SERVER=DEDICATED)" +
"(SERVICE_NAME={1})));" +
"User Id={2};" +
"Password={3};"; // assumes port is 1521 (the default)
return String.Format(format, host, serviceName, userId, password);
}
}
Final tip: If you encounter the error "System.Data.OracleClient requires Oracle client software version 8.1.7", see this question.
ODAC xcopy will get you away with about 45MB.
http://www.oracle.com/technology/software/tech/windows/odpnet/index.html
I found this post on the Oracle forum very usefull as well:
How to setup Oracle Instant Client with Visual Studio
Remark: the ADO.NET team is deprecating System.Data.OracleClient so for future projects you should use ODP.NET
Reproduction:
Setup the following environment variables:
make sure no other oracle directory is in your PATH
set your PATH to point to your instant client
set your TNS_ADMIN to point to where you tnsnames.ora file is
located
set your NLS_LANG
set your ORACLE_HOME to your instant client
For me, I set NLS_LANG to
http://download-east.oracle.com/docs/html/A95493_01/gblsupp.htm#634282
I verified this was using the correct client software by using the sqlplus add-on to the instant client.
For me, I set:
SET NLS_LANG=AMERICAN_AMERICA.WE8MSWIN1252
Note: before you make any changes, back up your Oracle registry key (if exist) and backup the string for any environment variables.
Read the Oracle Instant Client FAQ here

Categories

Resources