We have a WPF application need to display a new Outlook item, allow user to edit before send. The application start with administrator privileges, so if user's Outlook opened already, then there's an error when getting Outlook instance. How to solve it? Please help me, thanks.
You can't automate Outlook if it is run under different security context. But you can detect such cases trying to get an Outlook instance using the Marshal.GetActiveObject method which obtains a running instance of the specified object from the running object table (ROT). For example:
Outlook.Application GetApplicationObject()
{
Outlook.Application application = null;
// Check whether there is an Outlook process running.
if (Process.GetProcessesByName("OUTLOOK").Count() > 0)
{
// If so, use the GetActiveObject method to obtain the process and cast it to an Application object.
application = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
if(application == null)
MessageBox.Show("You need to run Outlook under the same security context");
}
else
{
// If not, create a new instance of Outlook and log on to the default profile.
application = new Outlook.Application();
Outlook.NameSpace nameSpace = application.GetNamespace("MAPI");
nameSpace.Logon("", "", Missing.Value, Missing.Value);
nameSpace = null;
}
// Return the Outlook Application object.
return application;
}
So, when the Outlook.exe process exists and you can't get the object all you can do is to ask users to run the application under the same security context.
There isn't much you can do short of starting your app in the same security context.
Related
I am developing a small project where I need to be able to retrieve emails from an Outlook mailbox in order to process the emails received.
I took an old program made in VB.net to rewrite it in C #.
With the project in Vb.net I have no errors
However I have this error: System.IO.FileNotFoundException : 'Could not load file or assembly 'office, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'. The specified file can not be found.' with the C# project.
My references :
When I compare the Microsoft.Office.Interop.outlook library to the project in Vb.net installed is exactly the same (same version, etc.).
So if you have an idea to solve this problem I am interested :D
You can try the following steps to solve the System.IO.FileNotFoundException problem.
First, you need to change the current framework to .NET Core 3.0.
Second, Select Interop.Microsoft.Office.Interop.Outlook, right click properties and set "Embed Interop Types" to "Yes".
Finally, you can use the following code to retrieve emails from an Outlook mailbox.
using Outlook = Microsoft.Office.Interop.Outlook;
Outlook.Application oApp = null;
oApp = new Outlook.Application();
Outlook.NameSpace nameSpace = oApp.GetNamespace("MAPI");
Outlook.Items items = null;
try
{
// use default profile and DO NOT pop up a window
// on some pc bill gates fails to login without the popup, then we must pop up and lets use choose profile and allow access
nameSpace.Logon("", "", false, Missing.Value);
var folder = nameSpace.Folders["emailaddress"].Folders["Folder"];
items = folder.Items;
foreach (Outlook.MailItem item in items)
{
Console.WriteLine(item.Subject);
}
Console.WriteLine("yes");
Console.ReadKey();
}
catch (Exception)
{
// use default profile and DO pop up a window
nameSpace.Logon("", "", true, true);
}
I am a long time c# developer but brand new to QBFC. I have downloaded the samples and was actually able to add an invoice to my file with it, but I am a little confused. I have trouble connecting unless QB is up and running. I was trying to follow the code in the sample, but it is difficult. I need this app to add invoices and bills to the file even if QB is not open. They only have one file so there won't be an instance where another file is already open. Also, the environment is simple as everything runs on the same computer.
My basic questions are:
How to select the correct QB file and provide credentials to allow access?
Is there a decent simple example using QBFC? Everything I have found is using XML which seems overly complicated compared to QBFC.
I cannot seem to get QB to open automatically. I have tried the code below and I get an error that states "Could not start QuickBooks".
Any pointers are greatly appreciated.
QBSessionManager qbSession = new QBSessionManager();
qbSession.OpenConnection("", "Lumber Management System");
try
{
qbSession.BeginSession("C:\\Users\\Jerry\\Documents\\QuickBooks\\Company Files\\MRJ Tecnology, LLC", ENOpenMode.omDontCare);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + '\n' + ex.StackTrace, "Error opening QB");
}
There are a couple of things that you need in order for this to work. The first time that you request access to a company file, QuickBooks must be opened and the Admin must be logged in. The Admin will then be given a dialog to grant permission to your application to access QuickBooks. In the permission dialog, it will ask the Admin if they want to allow the application to read and modify the company file with four options:
No
Yes, prompt each time
Yes, whenever this QuickBooks company file is open
Yes, always; allow access even if QuickBooks is not running
The admin must choose the fourth option to allow your app to launch QuickBooks without running.
I would also suggest that you use OpenConnection2 instead of OpenConnection, and use a unique ID as the first parameter. You will also need to specify the connection type, which should be ENConnectionType.ctLocalQBD.
It also appears that the filename you are passing in the BeginSession call does not include the .qbw extension. Here is a basic sample:
QBSessionManager SessionManager = null;
try
{
SessionManager = new QBSessionManager();
SessionManager.OpenConnection2("UniqueAppID", "Lumber Management System", ENConnectionType.ctLocalQBD);
SessionManager.BeginSession("C:\\Users\\Jerry\\Documents\\QuickBooks\\Company Files\\MRJ Tecnology, LLC.qbw", ENOpenMode.omSingleUser);
// CODE TO SEND TO QB GOES HERE
}
catch(Exception ex)
{
MessageBox.Show("Error opening QB:" + ex.ToString());
}
finally
{
if(SessionManager != null)
{
SessionManager.EndSession();
SessionManager.CloseConnection();
}
}
In my WPF app, I need to enable/disable functionality based on the team. The team information is configured as outlook distribution list. Now I need retrieve this information from my App.
I googled and found the link
http://msdn.microsoft.com/EN-US/library/office/ff184638(v=office.15).aspx
Unfortunately it doesn't compile as it is. After bit of research I can make it compile by changing it by changing it as
currentUser = new Outlook.Application().Session.CurrentUser.AddressEntry;
However, this works only when outlook is opened, but when outlook is closed it throws the exception. Any idea?
Finally I managed to crack it. Apparently we need to briefly start the outlook application, the solution is explained in the link
https://groups.google.com/forum/#!msg/microsoft.public.outlook.program_vba/lLJwbwwl-XU/gRuQYRpJtxEJ
Hence I modified my code GetCurrentUserMembership() slightly to accomadate this change. Now it's working good. Tested in outlook 2007 and 2010.
The complete solution,
private List<string> GetCurrentUserMembership()
{
Outlook.Application outlook = new Outlook.Application();
Outlook.MailItem oMsg = (Outlook.MailItem)outlook.CreateItem(Outlook.OlItemType.olMailItem);
Outlook.Inspector oInspector = oMsg.GetInspector;
//session.Logon("", "", false, false);
var sb = new List<string>();
Outlook.AddressEntry currentUser = outlook.Session.CurrentUser.AddressEntry;
if (currentUser.Type != "EX") return sb;
var exchUser = currentUser.GetExchangeUser();
if (exchUser == null) return sb;
var addrEntries = exchUser.GetMemberOfList();
if (addrEntries == null) return sb;
foreach (Outlook.AddressEntry addrEntry in addrEntries)
{
sb.Add(addrEntry.Name);
}
return sb;
}
Could you please be more specific? What exception (error message and error code) do you get in the code?
I'd recommend starting from breaking the chain of calls and declare each property or method call on a separate line. Thus, you will find a problematic property or method call which fires an exception.
Most probably you need to call the Logon method of the Namespace class. As an example, you may find the C# app automates Outlook (CSAutomateOutlook) sample project helpful.
I recently upgraded an MSTest project to .NET 4.0 and VS 2010. Several of the tests query an outside vendor service and thus prompt the user for necessary credentials to communicate through our corporate web proxy. This used to work fine in vs2008 but after the upgrade the dialog will only display if the user switches focus from VS to another app immediately after kicking off the tests. Is there anything special that needs to be done when displaying the dialog? The best I can figure is that there is some WPF caveat that got introduced with the redesign of VS.
The code in question
private void PromptUser()
{
if (!credentialsSet)
{
using (CredentialsDialog dialog = new CredentialsDialog(true))
{
Process process = Process.GetCurrentProcess();
IWin32Window window = Control.FromHandle(process.MainWindowHandle);
DialogResult dr = dialog.ShowDialog(window);
if (dr == DialogResult.Cancel)
{
throw new InvalidOperationException("Credentials not entered");
}
credentials = dialog.Credentials;
user = dialog.Username;
password = dialog.Password;
domain = dialog.Domain;
}
credentialsSet = true;
}
}
I would sugest that requiring the credentials is bad practice. Have you conidered adding your credentials in an excrypted fasion to the config of the test assembly.
You will run into a lot of problems if you try to setup this for automated build.
You may need to activate the dialog in order to get it to pop up over whatever programs you have active.
using (CredentialsDialog dialog = new CredentialsDialog(true))
{
Process process = Process.GetCurrentProcess();
IWin32Window window = Control.FromHandle(process.MainWindowHandle);
dialog.Activate();
DialogResult dr = dialog.ShowDialog(window);
// ...
}
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.activate.aspx
I have written a simple windows service which will launch a exe specified in the
onstart() method of the service. After starting the service the exe got launched it only
presents in the memory but it doesnt show in the explorer.
I'm trying to launch a calc.exe from my code.it shows the exe in the memory but it
doesnt comes into my view(i.e) in the explorer.
Below is my code to launch the exe in the onStart() method
Process pr=new Process();
pr.StartInfo.FileName="calc.exe";
pr.StartInfo.WindowStyle=ProcessWindowStyle.Maximized;
pr.StartInfo.CreateNoWindow=false;
pr.Start();
// pr.WaitForExit();
Services run in other session on Vista or later and applications started directly from services are started in the same session by default. Starting applications in other sessions is possible - you have to find the id of the user session and use CreateProcessAsUser.
If more than one user is logged in and you need to start your program for all users you must find the ids of all sessions.
Here is sample code:
int session = Win32.WTSGetActiveConsoleSessionId();
if (session == 0xFFFFFFFF)
{
return false;
}
IntPtr userToken;
bool res = Win32.WTSQueryUserToken(session, out userToken);
if (!res)
{
this.log.WriteEntry("Error WTSQueryUserToken");
return false;
}
string path = GetPath();
string dir = Path.GetDirectoryName(path);
Win32.STARTUPINFO si = new Win32.STARTUPINFO();
si.lpDesktop = "winsta0\\default";
si.cb = Marshal.SizeOf(si);
Win32.PROCESS_INFORMATION pi = new Win32.PROCESS_INFORMATION();
Win32.SECURITY_ATTRIBUTES sa = new Win32.SECURITY_ATTRIBUTES();
sa.bInheritHandle = 0;
sa.nLength = Marshal.SizeOf(sa);
sa.lpSecurityDescriptor = IntPtr.Zero;
if (!Win32.CreateProcessAsUser(userToken, // user token
path, // exexutable path
string.Empty, // arguments
ref sa, // process security attributes ( none )
ref sa, // thread security attributes ( none )
false, // inherit handles?
0, // creation flags
IntPtr.Zero, // environment variables
dir, // current directory of the new process
ref si, // startup info
out pi)) // receive process information in pi
{
int error = Marshal.GetLastWin32Error();
this.log.WriteEntry("Error CreateProcessAsUser:" + error);
return false;
}
Services are run under different account privileges (LocalService/NetworkService etc)and hence they don't have access to your desktop (under your login account's control).
Services are meant to do their job silently and thats what they should do. (with the exception of logging something in windows event log when they have something important to say)
If you open your service's properties window, go to the Log On tab then check the "Allow service to interact with desktop" check box you will get the behavior you want. Also depending on what app you what to run you may need to change the log on account.
Services are not interactive by definition, so you shouldn't expect any user interface elements to show when you launch an application from a service.
It's by design...
Like already mentioned from the others a windows service is "normally" running under a separate account ("LocalSystem" or "NetworkService"). This is the reason why you might no see the UI of the program started by your service. Also services are not intended to have a UI, they act as a background service.
But also note that starting a application by a service can be a high security risk, because the application is running with the same privileges than your service is. Normally this would be the local system account.
I don't know what your are trying to achieve with your service, but consider to use the autostart function of windows instead of a service to run your application.