{"id":193,"date":"2017-01-19T00:00:27","date_gmt":"2017-01-19T00:00:27","guid":{"rendered":"http:\/\/bloodforge.com\/?p=193"},"modified":"2020-02-20T04:18:22","modified_gmt":"2020-02-20T04:18:22","slug":"running-local-web-pages-in-cefsharp-wpf","status":"publish","type":"post","link":"https:\/\/bloodforge.azurewebsites.net\/index.php\/2017\/01\/19\/running-local-web-pages-in-cefsharp-wpf\/","title":{"rendered":"Running Local Web Pages in CefSharp.WPF"},"content":{"rendered":"\n<p>I needed to create a program which will run a web page from the local file system. Using the WebBrowser control wasn&#8217;t really an option since that uses the Trident engine which didn&#8217;t support many of the things we were trying to do in the web page. I went with the latest build of&nbsp;<a href=\"https:\/\/www.nuget.org\/packages\/CefSharp.Wpf\/\">CefSharp.Wpf<\/a>, but that doesn&#8217;t natively run local files, so it had to be modified. Fortunately, I found some posts on how to enable that feature (<a href=\"https:\/\/thechriskent.com\/2014\/04\/21\/use-local-files-in-cefsharp\/\">here<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/github.com\/cefsharp\/CefSharp\/blob\/master\/CefSharp.Example\/CefSharpSchemeHandler.cs\">here<\/a>) &#8211; however, the first link was for an older version of CefSharp, and the second link only works with static resource files. I ended up merging both concepts to get it to work.<\/p>\n\n\n\n<p>The code below works on version 53.0.1 of CefSharp.Wpf.&nbsp;<\/p>\n\n\n\n<p>First, the following two classes needed to be created:<\/p>\n\n\n\n<p>LocalResourceHandlerFactory.cs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using System;\nusing CefSharp;\nnamespace MyCustomNamespace\n{\n    public class LocalResourceHandlerFactory : IResourceHandlerFactory\n    {\n        public bool HasHandlers\n        {\n            get\n            {\n                return true;\n            }\n        }\n\n        public IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request)\n        {\n            return new LocalResourceHandler();\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p> LocalResourceHandler.cs: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using System;\nusing System.IO;\nusing CefSharp;\nusing System.Collections.Generic;\n\nnamespace MyCustomNamespace\n{\n    class LocalResourceHandler : IResourceHandler\n    {\n        private string mimeType;\n        private MemoryStream stream;\n\n        public void Cancel()\n        {\n\n        }\n\n        public bool CanGetCookie(Cookie cookie)\n        {\n            return true;\n        }\n\n        public bool CanSetCookie(Cookie cookie)\n        {\n            return true;\n        }\n\n        public void Dispose()\n        {\n\n        }\n\n        public void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl)\n        {\n            responseLength = stream == null ? 0 : stream.Length;\n            redirectUrl = null;\n\n            response.StatusCode = (int)System.Net.HttpStatusCode.OK;\n            response.StatusText = \"OK\";\n            response.MimeType = mimeType;\n        }\n\n        public bool ProcessRequest(IRequest request, ICallback callback)\n        {\n            \/\/ The 'host' portion is entirely ignored by this scheme handler.\n            var uri = new Uri(request.Url);\n            var file = uri.AbsolutePath;\n\n            if (File.Exists(file))\n            {\n                Byte&#91;] bytes = File.ReadAllBytes(file);\n                stream = new MemoryStream(bytes);\n                switch (Path.GetExtension(file))\n                {\n                    case \".html\":\n                    case \".htm\":\n                        mimeType = \"text\/html\";\n                        break;\n                    case \".css\":\n                        mimeType = \"text\/css\";\n                        break;\n                    case \".js\":\n                        mimeType = \"text\/javascript\";\n                        break;\n                    case \".png\":\n                        mimeType = \"image\/png\";\n                        break;\n                    case \".jpg\":\n                    case \".jpeg\":\n                        mimeType = \"image\/jpeg\";\n                        break;\n                    case \".gif\":\n                        mimeType = \"image\/gif\";\n                        break;\n                    case \".woff\":\n                        mimeType = \"application\/font-woff\";\n                        break;\n                    case \".eot\":\n                        mimeType = \"application\/vnd.ms-fontobject\";\n                        break;\n                    case \".ttf\":\n                        mimeType = \"application\/font-ttf\";\n                        break;\n                    case \".otf\":\n                        mimeType = \"application\/font-otf\";\n                        break;\n                    case \".svg\":\n                        mimeType = \"image\/svg+xml\";\n                        break;\n                    case \".appcache\":\n                    case \".manifest\":\n                        mimeType = \"text\/cache-manifest\";\n                        break;\n                    default:\n                        mimeType = \"application\/octet-stream\";\n                        break;\n                }\n                callback.Continue();\n                return true;\n            }\n            return false;\n        }\n\n        public bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)\n        {\n            \/\/Dispose the callback as it's an unmanaged resource, we don't need it in this case\n            callback.Dispose();\n\n            if (stream == null)\n            {\n                bytesRead = 0;\n                return false;\n            }\n\n            \/\/Data out represents an underlying buffer (typically 32kb in size).\n            var buffer = new byte&#91;dataOut.Length];\n            bytesRead = stream.Read(buffer, 0, buffer.Length);\n\n            dataOut.Write(buffer, 0, buffer.Length);\n\n            return bytesRead &amp;gt; 0;\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p> With the above code in place, we can now load the web page from a local file. The code below assumes that the HTML files are located in a &#8216;web&#8217; subdirectory (for example, \/bin\/debug\/web\/) <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public MainWindow()\n        {\n            \n            var settings = new CefSharp.CefSettings { RemoteDebuggingPort = 1234 };\n            \/\/ for some reason, performance sucks w\/ the gpu enabled\n            settings.CefCommandLineArgs.Add(\"disable-gpu\", \"1\");\n            settings.CefCommandLineArgs.Add(\"disable-gpu-vsync\", \"1\");\n            CefSharp.Cef.Initialize(settings);\n\n            InitializeComponent();\n\n            browser.ResourceHandlerFactory = new LocalResourceHandlerFactory();\n\n            browser.BrowserSettings.FileAccessFromFileUrls = CefSharp.CefState.Enabled;\n            browser.BrowserSettings.UniversalAccessFromFileUrls = CefSharp.CefState.Enabled;\n            browser.BrowserSettings.WebSecurity = CefSharp.CefState.Disabled;\n            \/\/browser.BrowserSettings.WebGl = CefSharp.CefState.Disabled;\n\n\n\n            string dir = System.IO.Directory.GetCurrentDirectory();\n            String page = string.Format(\"{0}\/web\/index.html\", dir);\n            browser.Address = page;\n        }<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I needed to create a program which will run a web page from the local file system. Using the WebBrowser control wasn&#8217;t really an option since that uses the Trident engine which didn&#8217;t support many of the things we were trying to do in the web page. I went with the latest build of&nbsp;CefSharp.Wpf, but [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":194,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,4],"tags":[],"_links":{"self":[{"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/posts\/193"}],"collection":[{"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/comments?post=193"}],"version-history":[{"count":1,"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/posts\/193\/revisions"}],"predecessor-version":[{"id":195,"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/posts\/193\/revisions\/195"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/media\/194"}],"wp:attachment":[{"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/media?parent=193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/categories?post=193"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bloodforge.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/tags?post=193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}