QuickBooks custom application deployment - c#

I have created an application in c# that connects to our QB in order to post invoice ans such. I am ready to deploy this to out accountants so they can further test the application. This app works fine on my machine, however, when I build my project and take the exe to one of our accountants computers I get an error about "COM class factory, blah, blah Class not registered"
In the past that error would mean that I need to build the c# with x86 and not x64, but I did and still receive the error. I now believe that they do not have the QBFC12.dll registered on their computer like I do.
Is this the problem, and if so then do I have to manually register the dll or is their another way to get that dll on their machine?

I found the answer finally.
If you installed QBSDK, explore that folder and locate the Tools folder
Under that you should find a Installers folder that has "QBFC12_0Installer.exe"
Install the exe on the client machine.
Try to run your program, if you encounter another error about "QBXML components are not installed, you will need to re-install QuickBooks and follow from step 1 again. This should get your application up and running for the client.
The person that came the closest and set me on the right track was MikeBr59 so I upvoted him, and posted this incase it helps anyone else.
Thanks,

I think you have to copy it manually and register.
There is no other alternative.
Follow the below steps to copy and register it manually.
1.copy "QBFC12.dll" file into following 2 folders :
a.C:\Windows\System32\
b.C:\Windows\SysWOW64\
2.register the "QBFC12.dll" file using following steps :
open Command Prompt
cd C:\Windows\System32
regsvr32 QBFC12.dll
3.while running the project from visual studio force the platform target as 32 bit -> as the project hasbeen developed in 32 bit using following steps:
Right click on Project
Select Properties
Goto Build Options
Change "Platform Target" from "ANY CPU" to "X86"
4.now run the Project.

Per the QuickBooks SDK docs
You can never redistribute the request processor DLL (qbxmlrp.dll or qbxmlrp2.dll). This violates the license agreement and can lead to undesirable user experiences.
NOTE: It is a violation of your qbXML license agreement to redistribute QBFC, RDS, the QBO connector, or the web connector without using either our stand-alone installers or our merge modules.
To solve this problem, I created the following static class:
public static class QbfcInstaller
{
/// <summary>
/// Installs QBFC if not registered
/// </summary>
public static void install()
{
bool installed = CheckIfInstalled();
if(!installed)
{
string path = #"\\192.168.1.4\App\Publish\QBFC15_0Installer.exe";
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
//This is required in order for the Process to be able to request Elevation
startInfo.UseShellExecute = true;
startInfo.FileName = path;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
//This is required in order for the Process to request Elevation
startInfo.Verb = "runas";
try
{
MessageBox.Show("In order to work properly the application needs to install the Quickbooks Connector Library.\nPlease DO NOT skip this step or it will cause errors latter."
, "Install QBFC 15.0", MessageBoxButtons.OK, MessageBoxIcon.Information);
using(Process installProcess = Process.Start(startInfo))
{
installProcess.WaitForExit();
}
}
catch(Exception ex)
{
MessageBox.Show("Unable to install QBFC Error: " + ex.Message);
Process.GetCurrentProcess().Kill();
}
}
}
/// <summary>
/// Checks if QBFC is registered
/// </summary>
/// <returns></returns>
static bool CheckIfInstalled()
{
try
{
IQBSessionManager sessionManager = new QBSessionManager();
}
catch(Exception ex)
{
if(ex.HResult == -2147221164)
{
return false;
}
}
return true;
}
}
Then, I modified Program.Main() to call QbfcInstaller.install() before starting the Application:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
QbfcInstaller.install();
Application.Run(new FrmLogin());
}
This basically checks if QBFC COM is registered by trying to create a QBSessionMenager. If an error occurs, it checks if ex.Hresult is -2147221164, which is the error that throws if it's not registered. If it's not registered, it then starts the QBFC installer from a local shared folder. (You can also download the installer from a web server and then run it.)

I solved the issue of my x64 application not being able to use QBFC12.dll over COM by creating a COM+ application and registering QBFC12.dll as components of that COM+ application in Windows Component Services.

Related

Cannot create a windows service with SC.exe using WPF App after its installation C#

I created a WPF Desktop Application as well as a Worker Service (all .NET 6.0 Preview 3), packed them in a .MSI Setup File using Microsoft Visual Studio Installer Projects extension, which installs the WPF Application on the machine.
While the application installs and functions correctly, I had to somehow implement the service installation which should run after the WPF Application would be installed. I created a function for that, which runs sc.exe as administrator and installs the service using Process.Start(), which looks like this:
private static void InstallService()
{
const string ServiceName = "SomeService";
var path = Path.GetFullPath(#".\SomeService.exe");
var psi = new ProcessStartInfo
{
FileName = #"C:\Windows\system32\sc.exe",
Arguments = $"create { ServiceName } binPath= { path } start= auto",
Verb = "runas",
UseShellExecute = true,
};
try
{
Process.Start(psi);
}
catch (Exception ex)
{
MessageBox.Show($"Installation has failed: { ex.Message + ex.StackTrace }");
}
}
The problem with this function is that it executes properly when the application is ran in Visual Studio and when it is ran from the 'bin\Release' folder created by Visual Studio. The service is then installed and can be started. When the program, however, is installed using the .MSI package, the service does not install and no MessageBox is displayed, which shows that no exceptions are thrown.
What I have tried:
When the function is executed, a UAC prompt is shown and then the
process starts. I tried running the entire application as
administrator, but that didn't solve the issue.
I also tried copying all the files from the 'bin\Release' directory into the one in which the application is installed and replacing every file with the one from 'bin\Release', so that both directories should be the same, but that also didn't solve the issue.
After the installation function is executed, the service should start with another function for starting it:
private static void RunService()
{
const string ServiceName = "SomeService";
var psi = new ProcessStartInfo
{
FileName = #"C:\Windows\system32\sc.exe",
Arguments = $"start { ServiceName }",
Verb = "runas",
UseShellExecute = true,
};
try
{
Process.Start(psi);
}
catch (Exception ex)
{
MessageBox.Show($"Running was not approved or failed: { ex.Message }");
}
}
This function, however, functions correctly in both cases, although obviously only when the service is previously installed, which cannot be done in the .MSI installed application. As for the use of Process.Start() instead of ServiceController class, the application should not run as administrator by default, and it is not possible with the ServiceController, so I used Process.Start() with Verb = "runas" which runs the process as administrator only showing the UAC prompt when it is needed (starts the service only when it is not already running).
Is there any way to solve this problem and install a Worker Service in a .MSI installed WPF Application?
As I further proceeded to analyze all the possible factors, I finally noticed what was causing the issue in this case.
Generally, the paths generated by Visual Studio don't have any spaces, and because of that they can be written as a command argument without double quotes. In my case, the paths which contained the Project files also didn't have any spaces, which caused the commands without double quotes to be executed normally. The installation path, however, did contain spaces, as it's designed to be more user-friendly, which caused this piece of code to not execute as intended:
// the path parameter in the command will end when there will be a space somewhere in the path
Arguments = $"create { ServiceName } binPath= { path } start= auto"
The path variable only contains the full path, which is not wrapped in double quotes.
To prevent the issue the use of double quotes is necessary, and including the \ symbol notifies the compiler that these double quotes are a part of the string:
// the path parameter is wrapped in double quotes and will be fully read
Arguments = $"create { ServiceName } binPath= \"{ path }\" start= auto"
When the path would be fully written in the code, the missing double quotes are easy to notice. When, however, using string interpolation, it may cause problems, as it did in my case.

Is there any way to create a setup which update the current version of an application?

I created a simple C# WPF program, which run various setup of different program by clicking on buttons, so it automates some task when I have to configure PCs.
Initially, I used the function Publish of VS, so when I do changes to the code and deploy again the user click on install update and he has the latest version (fantastic), but this application require admin rights because it install features, create folders such as I wrote above, so I add to my project the app.manifest for running my app with the correct rights. I published again the project, but I got an error, I read that <requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> option and ClickOnce can't exists at the same time, I found some workaround but not very functional, below the code:
public MainWindow()
{
InitializeComponent();
AdminRelauncher();
}
private void AdminRelauncher()
{
if (!IsRunAsAdmin())
{
ProcessStartInfo proc = new ProcessStartInfo();
proc.UseShellExecute = true;
proc.WorkingDirectory = Environment.CurrentDirectory;
proc.FileName = Assembly.GetEntryAssembly().CodeBase;
proc.Verb = "runas";
try
{
Process.Start(proc);
Application.Current.Shutdown();
}
catch (Exception ex)
{
MessageBoxResult mess = MessageBox.Show("Program must be run as admin! \n\n" + ex.ToString());
}
}
}
private bool IsRunAsAdmin()
{
try
{
WindowsIdentity id = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(id);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
catch (Exception)
{
return false;
}
}
After that i create a setup using Microsoft VS Installer Projects, it works well but every time I do changes or fixes to the code I have to deploy the setup -> unistall the old program -> install the new, this is a waste of time.
So there is any way to deploy a sort of installer or something else which check for application update?
Or better run as administrator the application installed with ClickOnce?
It is best you create another app, a Launcher, whose purpose is to check if a local file on your other app have a version number below the one stored at your database/ftp. If it is true, then your Launcher will download from your ftp the new version and overwrite the old one. It is like I work on my current C# projects :P
Of course, you can also make your Launcher the main executable, at the end of the update check, it can run your program, and thats all, your users only have to run the launcher to update and/or run your main program.

How to silent install an external NSIS exe in WPF C#

Like the title says, i'm trying to install an .exe silently (say, for example, inkscape) through my application written in WPF C#. The problem is, the install process i use always return an exception and fails.
Here is the code that i use :
public static bool StartInstall(string pathtofile)
{
try
{
Process process = new Process();
process.StartInfo.FileName = "Temp.exe";
process.StartInfo.Arguments = string.Format(" /S", pathtofile);
process.Start();
process.WaitForExit();
Console.WriteLine("Silent Install was successful.");
return true;
}
catch
{
MessageBox.Show("Error!");
return false;
}
}
According to this website, the switch for a silent install for NSIS packaged exes is /S. I'm not sure whether or not i'm doing something wrong in the syntax, though.
The code i'm using come from this stackoverflow post. It does work for a .msi package. Maybe it does not work for NSIS exes?
I'm relatively clueless as to why it does not work. The code above will crash at the "process.Start()" line, most probably because of a unknown command or something.
I'd be thankful if anybody could shred a bit of light as to how to launch that process for a NSIS installer.
I have solved the problem with the help of csharpfolk.
My problem was a combination of two different cause :
First, Admin privileges were indeed required to run the application.
Second, the line "process.StartInfo.Filename" requires the full path to the file. As soon as i changed it to "C:\Downloads\Temp.exe", it worked.

C# .Net Service won't install on Win 7 64 bit

.Net 3.5
I've built into the service exe the ability for it to install itself using a -i feature. I have a custom installer class and am using a common technique on found online here. That installer class basically has it's own service and serviced process installer.
This code has worked well for a very long time. Finally ran into a Win 7 64 bit machine were it refuses to install.
Basically, the log shows it's installing the service and that succeeds. Then it tries to create an event log and that fails with
An exception occurred during the Install phase.
System.ComponentModel.Win32Exception: The specified service already
exists
I just got done having the OS completely reinstalled from scratch, first thing I did was try to install as a service, and it's the same error. Why is it thinking that event log is already there?
I've already read all the other posts and I've browsed my registry and there is nothing in there for my service or event log. I have full admin rights, when I try to open cmd as administrator, it doesn't even prompt me, so as far as I can tell, I am an admin (I can see that in my user profile).
I even added code to check to see if it found the EventLog using System.Diagnostics.EventLog.SourceExists which does report it found it, and so I added a call to System.Diagnostics.EventLog.DeleteEventSource but that doesn't help.
I even tried removing the EventLog installer from the ServiceInstaller, but then it starts failing for other reasons.
Any ideas?
Here is some sample code for an alternate installer I tried that I found here with the same results:
public partial class Service1Installer : Installer
{
public Service1Installer()
{
InitializeComponent();
ServiceProcessInstaller process = new ServiceProcessInstaller();
process.Account = ServiceAccount.LocalSystem;
ServiceInstaller serviceAdmin = new ServiceInstaller();
serviceAdmin.StartType = ServiceStartMode.Manual;
serviceAdmin.ServiceName = "Service1";
serviceAdmin.DisplayName = "Service1";
serviceAdmin.Description = "Service1";
Installers.Add(serviceAdmin);
Installers.Add(process );
}
}
uninstall your service
installutil /u yourproject.exe
restart your machine
http://msdn.microsoft.com/en-us/library/sd8zc8ha(v=vs.80).aspx
let me know if you still have a issue
Use installutil as #MicahArmantrout mentions, if the exe still resides on disk.
Otherwise, open a commandline as Administrator and execute: sc delete "my service name"
In the end, my problem was our internal installer. I commented it out and now just install the service from the command line and it now installs on 64 bit OS. Still don't know why it would work before on 32 bit.

Uninstalling application written in c# using setup and deployment and Installer class

my program installs my custom service and register it. Now what I am trying to do is to unregister the service and delete all files after uninstall. I am using Visual Studio and Setup and Deployment and the Installer class, I have overridden a few methods that I am presenting below:
protected override void OnAfterUninstall(IDictionary savedState)
{
base.OnAfterUninstall(savedState);
string directory = "C:\\Program Files (x86)\\MyService\\";
if (System.IO.Directory.Exists(directory))
{
string[] files = System.IO.Directory.GetFiles(directory);
foreach (string file in files)
{
System.IO.File.Delete(file);
}
System.IO.Directory.Delete(directory);
}
}
protected override void OnBeforeUninstall(IDictionary savedState)
{
base.OnBeforeUninstall(savedState);
string path = "-u \"C:\\Program Files (x86)\\MyService\\AA_service.exe\"";
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "C:\\Program Files (x86)\\MyService\\InstallUtil.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = path;
Process.Start(startInfo);
}
It does not unregister service and it does not delete application folder. Can anyone suggest me what am I doing wrong?
//edit
now it is trying to remove files but I am getting access denied error on uninstall. Files I am trying to delete are .exe, .dll and some others
Did you add the custom actions into the MSI? If you don't have a custom action fire for your uninstall event then I'm not sure if these events will be called. Is there any reason why you are using the before and after install events instead of overriding the "uninstall" command?
If you don't call the Install function for the component, then the installer won't call the uninstall function either. You can program a messagebox into the uninstall (or a System.Diagnostics.Debugger.Attach()) if you want to see whether the code is executing or not.
Also as a matter of portability, I highly recommend that you use the Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) command to find the program files directory. This will port across x64 and x86 as well as any future revisions to the program files directory in the future.
Secondly I would use the Path.Combine function as well to safely merge the folders together.
EDIT:
I think you may be misusing the installation procedure. The custom action of the installer is to "register" the service once it has been installed by the MSI. It is also to unregister the service before it is then deleted by the MSI.
Go get a copy of WIX or use the MSI builder in Visual Studio. Drop your project output for your service into the project, setup the custom actions to call on your service exe and then the MSI will handle the install/uninstall for you. THe custom action will be called and register/unregister your service with the cache.
Be very careful though, if you need to upgrade there is a bug in the behaviour of the service installer, it is not able to succesfully upgrade or downgrade without you wiring the MSI properly to handle all the sequences that can occur.
using system.Threading;
static void Main()
{
string[] arguments = Environment.GetCommandLineArgs();
foreach (string argument in arguments)
{
if (argument.Split('=')[0].ToLower() == "/u")
{
ThreadStart starter = delegate { WorkThreadFunction(argument.Split('=')[1]); };
new Thread(starter).Start();
Thread.Sleep(30000);
Application.Exit();
return;
}
}
}

Categories

Resources