无法从托管 wcf 的 Windows 服务启动 WinWord

Posted

技术标签:

【中文标题】无法从托管 wcf 的 Windows 服务启动 WinWord【英文标题】:Can't launch WinWord from windows service hosted wcf 【发布时间】:2015-10-09 11:30:38 【问题描述】:

我有这个 WCF。当我像 http://localhost:8733/doc/run/Test.doc 这样从 Chrome 浏览器调用它时,wcf 服务返回:

Tect.doc Succeed

但是 Word 的窗口没有出现。我应该更改哪些代码才能打开 Word 的窗口?

namespace WordDavService

    [ServiceContract]
    public interface IWcfDocOpen
    
        [WebGet(UriTemplate = "/run/Path", ResponseFormat = WebMessageFormat.Json)]
        [OperationContract]
        string Run(string Path);
    

    [AspNetCompatibilityRequirements(RequirementsMode =
        AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
    public class DocOpenWcfService : IWcfDocOpen
    
        //public static void Main() 

        public string Run(string Path)
        
            Task<string> thread = Task<string>.Factory.StartNew(() =>
                return DocOpenWcfService.OpenWord(Path); );
            Task.WaitAll(thread);
            return Path+thread.Result;
        

        protected static string OpenWord(string Path)
        
            Word._Application application = null; ;
            Word._Document document = null; ;

            Object _Path = Path;
            try
            
                application = new Word.Application();
                if (!string.IsNullOrEmpty(_Path.ToString()))
                    document = application.Documents.Open(ref _Path);
                application.Visible = true;
            
            catch (Exception error)
            
                try
                
                    document.Close();
                
                catch  
                try
                
                    application.Quit();
                
                catch  
                document = null;
                application = null;
                return error.Message+"innerExeption: "+error.InnerException.Message;
            
            return "Succeed";
        
    

【问题讨论】:

为什么要在服务器上打开交互式桌面应用程序? 我有指向 WebDav .docx 文档的链接,我想直接在 Word 中打开此文档。这不是服务器,它只是托管在客户端机器上的 Windows 服务。 所以你的意思是这个词没有在调用机器上打开,或者那个词没有在运行windows服务的机器上打开? 这是同一台机器。在客户端机器上使用 wcf 托管 Windows 服务。当有人像localhost:8733/doc/run/Test.doc 这样调用这个 wcf 时,它应该在本地启动 Word,并使用一个文件名作为 Url 中的参数传递 【参考方案1】:

我不确定您指的是什么错误,但是您应该能够通过在尝试打开这些文件之前检查这些文件的存在来防止出现有关不可访问文件等的错误? Word 自动化通常涉及大量这样的防御性编程。

但是,我认为你有一个更大的问题。从 Windows 服务自动执行 Word 是“不受欢迎的”,因为它不受 Microsoft 支持,并且可能违反许可条款。

可以做到(很多人都这样做 - 我也是),但一般来说,人们会在没有登录用户且可能也在使用 Office 的专用服务器上执行此操作。如果在用户的 PC 上执行此操作,我会非常紧张,因为您的服务和用户都可能同时运行 Word。

如果您仍想采用这种方法,如果您能描述您所看到的具体错误或不稳定性,将会很有帮助。

另请参阅this answer,这可能会让您更进一步。

【讨论】:

当 Wrod 启动时,它会警告 2 个错误,它无法保存文件 MSO1049.ad 和 MSO.ACLMSO1049.ad 因为可能我没有权限执行此操作或文件具有只读访问权限.之后,Word 不会打开我通过此代码中的输入参数 _Path 指定给它的文件:'document = application.Documents.Open(ref _Path,Type.Missing,(object)false);'除此之外,我无法使用 Word 的界面打开文件,但可以创建新文件。而且我不能只使用任务管理器关闭 Word 的窗口。【参考方案2】:

这是explanation 如何从 Windows 服务启动 GUI 进程。

这是我的领悟:

namespace WordDavService


[ServiceContract]
public interface IWcfDocOpen


    [WebGet(UriTemplate = "/run/Path", ResponseFormat = WebMessageFormat.Json)]
    [OperationContract]
    string Run(string Path);


[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class DocOpenWcfService : IWcfDocOpen

    //public static void Main() 

    public string Run(string _Path)
    

        int session = WinAPI.WTSGetActiveConsoleSessionId();
        if (session ==0xFFFFFFFF)
        
            return "NoActiveSession";
        

        IntPtr userToken;
        bool res = WinAPI.WTSQueryUserToken(session, out userToken);


        string path =  "C:\\Windows\\LauncherWord.exe";
        string dir = Path.GetDirectoryName(path);
       WinAPI.STARTUPINFO si = new WinAPI.STARTUPINFO();
        si.lpDesktop = "winsta0\\default";
        si.cb = (uint)Marshal.SizeOf(si);

        WinAPI.PROCESS_INFORMATION pi = new WinAPI.PROCESS_INFORMATION();
        WinAPI.SECURITY_ATTRIBUTES sa = new WinAPI.SECURITY_ATTRIBUTES();
        sa.bInheritHandle = true;
        sa.length = Marshal.SizeOf(sa);
        sa.lpSecurityDescriptor = IntPtr.Zero;

        if (!WinAPI.CreateProcessAsUser(userToken,       // user token
                                        path+" "+_Path,           // exexutable path
                                        "",   // arguments
                                        ref sa,         // process security attributes ( none )
                                        ref sa,         // thread  security attributes ( none )
                                        true,          // inherit handles?
                                        0x02000000,              // creation flags
                                        IntPtr.Zero,    // environment variables
                                        dir,            // current directory of the new process
                                        ref si,         // startup info
                                        ref pi))        // receive process information in pi
        
            int error = Marshal.GetLastWin32Error();
            return "Error CreateProcessAsUser:" + error + " File: " + path + " " + _Path;
        
        else
            return "Success:" + path + " " + _Path;

    



public static class WinAPI

    [DllImport("Kernel32.dll", SetLastError = true)]
    public static extern int WTSGetActiveConsoleSessionId();

   [DllImport("wtsapi32.dll", SetLastError = true)]
    public static extern bool WTSQueryUserToken(int Session,[Out] out IntPtr phToken);

    public struct PROCESS_INFORMATION
    
        public IntPtr hProcess;
        public IntPtr hThread;
        public uint dwProcessId;
        public uint dwThreadId;
    

    public struct STARTUPINFO
    
        public uint cb;
        public string lpReserved;
        public string lpDesktop;
        public string lpTitle;
        public uint dwX;
        public uint dwY;
        public uint dwXSize;
        public uint dwYSize;
        public uint dwXCountChars;
        public uint dwYCountChars;
        public uint dwFillAttribute;
        public uint dwFlags;
        public short wShowWindow;
        public short cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    

    public struct SECURITY_ATTRIBUTES
    
        public int length;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    

  [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser",      SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern bool  CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, 
                      ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, 
                      bool bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvrionment,
                      string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, 
                      ref PROCESS_INFORMATION lpProcessInformation);

    

此服务会启动 LauncherWord.exe,它会像这样启动 Word:

namespace LauncherWord

class Program

    static void Main(string[] args)
    
        string Path = "";
        if (args.Length > 0)
            Path = args[0];
        OpenWord(Path);

    

    protected static string OpenWord(string Path)
    
        Word._Application application = null; ;
        Word._Document document = null; ;

        Object _Path = Path;
        try
        
            application = new Word.Application();

            if (!string.IsNullOrEmpty(_Path.ToString()))
                document = application.Documents.Open(ref _Path,Type.Missing,(object)false);
            application.Visible = true;

        
        catch (Exception error)
        
            try
            
                document.Close();
            
            catch  
            try
            
                application.Quit();
            
            catch  
            document = null;
            application = null;
            return error.Message + "innerExeption: " + error.InnerException.Message;
        

        return "Succed";
    
  

但结果并不好,因为 Word 开头有几个关于不可访问文件的错误。并且 Word 的行为不稳定,可能无法访问。

有没有人可以建议我如何使 Word 的工作稳定?

【讨论】:

以上是关于无法从托管 wcf 的 Windows 服务启动 WinWord的主要内容,如果未能解决你的问题,请参考以下文章

Windows 服务(托管 WCF 服务)在启动时立即停止

从作为 LocalSystem 运行的 WCF 托管服务以特定用户身份启动进程

通过 Windows 服务托管 WCF 服务库获得 System.InvalidOperationException:尝试启动 WCF 服务

从主机 WinForms 程序访问自托管 WCF 服务

Azure 功能无法访问托管在内部 windows 服务器中的 WCF 服务

如何从 IIS 托管的 WCF 服务启动进程?