Строка
Cells(NextRow, "E").Value = olApt.recipients.Address
должна быть заменена на
.Cells(NextRow, "E").Value = recip.Address
Также имейте в виду, что Outlook Security может блокировать доступ к таким свойствам, как SenderEmailAddress
или Recipients
, если антивирусное приложение не установлено или устарело. См. https://docs.microsoft.com/en-us/office/vba/outlook/how-to/security/security-behavior-of-the-outlook-object-model
Я нашел способ добавления параметров старта на сервисном установке:
Это не возможно сделать в управляемом коде.
Но существует одно достойное решение все же. Если все, что Вы хотите, имеет тот же исполняемый файл для сервиса окон и GUI (наиболее распространенный сценарий). Вам даже не нужны параметры. Просто регистрация Основного метода для System.Environment.UserInteractive
свойство и решает, что сделать...
static void Main(string[] args)
{
if (System.Environment.UserInteractive)
{
// start your app normally
}
else
{
// start your windows sevice
}
}
Параметры могут быть установлены P/Invoking ChangeServiceConfig API. Они появляются после заключенного в кавычки пути и имени файла к Вашему исполняемому файлу в lpBinaryPathName аргументе.
Параметры будут сделаны доступными для Вашего сервиса, когда он запустится через Основной метод:
static void Main(string[] args)
(Основной традиционно расположен в файле по имени Program.cs).
Следующие шоу, как Вы могли бы изменить установщик для вызова этого API после нормальных сервисных выполнений логики установки. Части этого, что, скорее всего, необходимо будет изменить, находятся в конструкторе.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration.Install;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
namespace ServiceTest
{
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
private string _Parameters;
private ServiceProcessInstaller _ServiceProcessInstaller;
private ServiceInstaller _ServiceInstaller;
public ProjectInstaller()
{
_ServiceProcessInstaller = new ServiceProcessInstaller();
_ServiceInstaller = new ServiceInstaller();
_ServiceProcessInstaller.Account = ServiceAccount.LocalService;
_ServiceProcessInstaller.Password = null;
_ServiceProcessInstaller.Username = null;
_ServiceInstaller.ServiceName = "Service1";
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
_ServiceProcessInstaller,
_ServiceInstaller});
_Parameters = "/ThisIsATest";
}
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
IntPtr hScm = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if (hScm == IntPtr.Zero)
throw new Win32Exception();
try
{
IntPtr hSvc = OpenService(hScm, this._ServiceInstaller.ServiceName, SERVICE_ALL_ACCESS);
if (hSvc == IntPtr.Zero)
throw new Win32Exception();
try
{
QUERY_SERVICE_CONFIG oldConfig;
uint bytesAllocated = 8192; // Per documentation, 8K is max size.
IntPtr ptr = Marshal.AllocHGlobal((int)bytesAllocated);
try
{
uint bytesNeeded;
if (!QueryServiceConfig(hSvc, ptr, bytesAllocated, out bytesNeeded))
{
throw new Win32Exception();
}
oldConfig = (QUERY_SERVICE_CONFIG) Marshal.PtrToStructure(ptr, typeof(QUERY_SERVICE_CONFIG));
}
finally
{
Marshal.FreeHGlobal(ptr);
}
string newBinaryPathAndParameters = oldConfig.lpBinaryPathName + " " + _Parameters;
if (!ChangeServiceConfig(hSvc, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
newBinaryPathAndParameters, null, IntPtr.Zero, null, null, null, null))
throw new Win32Exception();
}
finally
{
if (!CloseServiceHandle(hSvc))
throw new Win32Exception();
}
}
finally
{
if (!CloseServiceHandle(hScm))
throw new Win32Exception();
}
}
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern IntPtr OpenSCManager(
string lpMachineName,
string lpDatabaseName,
uint dwDesiredAccess);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern IntPtr OpenService(
IntPtr hSCManager,
string lpServiceName,
uint dwDesiredAccess);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
private struct QUERY_SERVICE_CONFIG {
public uint dwServiceType;
public uint dwStartType;
public uint dwErrorControl;
public string lpBinaryPathName;
public string lpLoadOrderGroup;
public uint dwTagId;
public string lpDependencies;
public string lpServiceStartName;
public string lpDisplayName;
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool QueryServiceConfig(
IntPtr hService,
IntPtr lpServiceConfig,
uint cbBufSize,
out uint pcbBytesNeeded);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeServiceConfig(
IntPtr hService,
uint dwServiceType,
uint dwStartType,
uint dwErrorControl,
string lpBinaryPathName,
string lpLoadOrderGroup,
IntPtr lpdwTagId,
string lpDependencies,
string lpServiceStartName,
string lpPassword,
string lpDisplayName);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseServiceHandle(
IntPtr hSCObject);
private const uint SERVICE_NO_CHANGE = 0xffffffffu;
private const uint SC_MANAGER_ALL_ACCESS = 0xf003fu;
private const uint SERVICE_ALL_ACCESS = 0xf01ffu;
}
}
По какой-то странной причине моя структура QUERY_SERVICE_CONFIG не получала полное значение lpBinaryPathName, только первый символ. Изменение его на класс ниже, похоже, решило проблему. Полный код находится на http://www.pinvoke.net/default.aspx/advapi32/QueryServiceConfig.html
Изменить: Также обратите внимание, что это устанавливает «Путь к исполняемому файлу» службы Windows, но не устанавливает «Параметры запуска» службы Windows.
[StructLayout(LayoutKind.Sequential)]
public class QUERY_SERVICE_CONFIG
{
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
public UInt32 dwServiceType;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
public UInt32 dwStartType;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
public UInt32 dwErrorControl;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpBinaryPathName;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpLoadOrderGroup;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]
public UInt32 dwTagID;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpDependencies;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpServiceStartName;
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
public String lpDisplayName;
}
Существует управляемый способ добавления параметров запуска к службам (не в разделе «Параметры запуска» / «Параметры запуска» консоли управления (services.msc), но в «Путь к исполняемому файлу» / «Pfad zur EXE-Datei», как это делают все собственные службы Windows).
Добавьте следующий код в свой подкласс System.Configuration.Install.Installer: (в C # -дружественном VB- Код)
'Just as sample
Private _CommandLineArgs As String() = New String() {"/Debug", "/LogSection:Hello World"}
''' <summary>Command line arguments without double-quotes.</summary>
Public Property CommandLineArgs() As String()
Get
Return _CommandLineArgs
End Get
Set(ByVal value As String())
_CommandLineArgs = value
End Set
End Property
Public Overrides Sub Install(ByVal aStateSaver As System.Collections.IDictionary)
Dim myPath As String = GetPathToExecutable()
Context.Parameters.Item("assemblypath") = myPath
MyBase.Install(aStateSaver)
End Sub
Private Function GetPathToExecutable() As String
'Format as something like 'MyService.exe" "/Test" "/LogSection:Hello World'
'Hint: The base class (System.ServiceProcess.ServiceInstaller) adds simple-mindedly
' a double-quote around this string that's why we have to omit it here.
Const myDelimiter As String = """ """ 'double-quote space double-quote
Dim myResult As New StringBuilder(Context.Parameters.Item("assemblypath"))
myResult.Append(myDelimiter)
myResult.Append(Microsoft.VisualBasic.Strings.Join(CommandLineArgs, myDelimiter))
Return myResult.ToString()
End Function
Удачи!
чха