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

时间:2015-10-09 11:30:39

标签: c# .net wcf ms-word

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

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";
        }
    }
}

2 个答案:

答案 0 :(得分:0)

这是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稳定工作?

答案 1 :(得分:0)

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

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

它可以完成(很多人都这样做 - 就像我一样),但一般来说人们会在没有登录用户且也可能正在使用Office的专用服务器上执行此操作。我会非常担心在用户的PC上执行此操作,其中您的服务和用户可能同时运行Word。

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

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