wpf dispacher 实现原理分析

一,作用

提供线程队列服务,通过windows message 这个基本原理逻辑代码。

二,代码

异步调用

dispath 代码 :https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs,9e84b372d672e449,references

dispacher 把我们行为封装成为DispatcherOperation,然后投递队列,同时投递事件给绑定的线程对应的 message only 窗口,窗口收到消息就运行我的行为。

写c++开发时候,为了拥有这样功能,我们都要自己写窗口,封装异步行为,到了WPF 这个已经是自带功能。

private void InvokeAsyncImpl(DispatcherOperation operation, CancellationToken cancellationToken)
        {
            DispatcherHooks hooks = null;
            bool succeeded = false;
 
            // Could be a non-dispatcher thread, lock to read
            lock(_instanceLock)
            {
                if (!cancellationToken.IsCancellationRequested &&
                    !_hasShutdownFinished &&
                    !Environment.HasShutdownStarted)
                {
                    // Add the operation to the work queue
                    //当前行为添加到队列里面去
                    operation._item = _queue.Enqueue(operation.Priority, operation);
 
                    // Make sure we will wake up to process this operation.
                    //投递消息给创建window,这样子处理消息
                    succeeded = RequestProcessing();
 
                    if (succeeded)
                    {
                        // Grab the hooks to use inside the lock; but we will
                        // call them below, outside of the lock.
                        hooks = _hooks;
                    }
                    else
                    {
                        // Dequeue the item since we failed to request
                        // processing for it.  Note we will mark it aborted
                        // below.
                        _queue.RemoveItem(operation._item);
                    }
                }
            }
 
            if (succeeded == true)
            {
                // We have enqueued the operation.  Register a callback
                // with the cancellation token to abort the operation
                // when cancellation is requested.
                if(cancellationToken.CanBeCanceled)
                {
                    CancellationTokenRegistration cancellationRegistration = cancellationToken.Register(s => ((DispatcherOperation)s).Abort(), operation);
 
                    // Revoke the cancellation when the operation is done.
                    operation.Aborted += (s,e) => cancellationRegistration.Dispose();
                    operation.Completed += (s,e) => cancellationRegistration.Dispose();
                }
 
                if(hooks != null)
                {
                    hooks.RaiseOperationPosted(this, operation);
                }
 
                if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
                {
                    EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextPost, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, operation.Priority, operation.Name, operation.Id);
                }
            }
            else
            {
                // We failed to enqueue the operation, and the caller that
                // created the operation does not expose it before we return,
                // so it is safe to modify the operation outside of the lock.
                // Just mark the operation as aborted, which we can safely
                // return to the user.
                operation._status = DispatcherOperationStatus.Aborted;
                operation._taskSource.SetCanceled();
            }
        }

创建dispacher

  1. 添加全局list dispacher【全局管理】
  2. 创建MessageOnlyHwndWrappe 窗口,这个是核心的功能。
  3. 添加 窗口消息处理函数 WndProcHook
     [SecurityCritical, SecurityTreatAsSafe]
        private Dispatcher()
        {
            _queue = new PriorityQueue<DispatcherOperation>();
 
            _tlsDispatcher = this; // use TLS for ownership only
            _dispatcherThread = Thread.CurrentThread;
 
            // Add ourselves to the map of dispatchers to threads.
            lock(_globalLock)
            {
                //全局list 增加 当前的list
                _dispatchers.Add(new WeakReference(this));
            }
 
            _unhandledExceptionEventArgs = new DispatcherUnhandledExceptionEventArgs(this);
            _exceptionFilterEventArgs = new DispatcherUnhandledExceptionFilterEventArgs(this);
 
            _defaultDispatcherSynchronizationContext = new DispatcherSynchronizationContext(this);
 
            // Create the message-only window we use to receive messages
            // that tell us to process the queue.
            MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();
            _window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window );
 
            _hook = new HwndWrapperHook(WndProcHook);
            _window.Value.AddHook(_hook);
 
            // DDVSO:447590
            // Verify that the accessibility switches are set prior to any major UI code running.
            AccessibilitySwitches.VerifySwitches(this);
        }

WndProcHook

处理窗口消息,用来处理 我们投递过来的异步操作

       private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            WindowMessage message = (WindowMessage)msg;
            if(_disableProcessingCount > 0)
            {
                throw new InvalidOperationException(SR.Get(SRID.DispatcherProcessingDisabledButStillPumping));
            }
 
            if(message == WindowMessage.WM_DESTROY)
            {
                if(!_hasShutdownStarted && !_hasShutdownFinished) // Dispatcher thread - no lock needed for read
                {
                    // Aack!  We are being torn down rudely!  Try to
                    // shut the dispatcher down as nicely as we can.
                    ShutdownImpl();
                }
            }
            else if(message == _msgProcessQueue)
            {
                //如果消息,异步投递消息
                //这个就是我们用dispacher beginInvoke的
                // 内部调用 RequestProcessing(); 投递回调消息过来
                ProcessQueue();
            }
            else if(message == WindowMessage.WM_TIMER && (int) wParam == TIMERID_BACKGROUND)
            {
                // This timer is just used to process background operations.
                // Stop the timer so that it doesn't fire again.
                SafeNativeMethods.KillTimer(new HandleRef(this, hwnd), TIMERID_BACKGROUND);
 
                ProcessQueue();
            }
            else if(message == WindowMessage.WM_TIMER && (int) wParam == TIMERID_TIMERS)
            {
                // We want 1-shot only timers.  So stop the timer
                // that just fired.
                KillWin32Timer();
 
                PromoteTimers(Environment.TickCount);
            }
 
            // We are about to return to the OS.  If there is nothing left
            // to do in the queue, then we will effectively go to sleep.
            // This is the condition that means Idle.
            DispatcherHooks hooks = null;
            bool idle = false;
 
            lock(_instanceLock)
            {
                idle = (_postedProcessingType < PROCESS_BACKGROUND);
                if (idle)
                {
                    hooks = _hooks;
                }
            }
 
            if (idle)
            {
                if(hooks != null)
                {
                    hooks.RaiseDispatcherInactive(this);
                }
 
                ComponentDispatcher.RaiseIdle();
            }
 
            return IntPtr.Zero ;
        }

获取当前dispatcher

  1. 通过FromThread 函数获取 当前线程的dispacher
  2. 如果当前线程不存在dispacher,那么直接创建。
       public static Dispatcher CurrentDispatcher
        {
            get
            {
                // Find the dispatcher for this thread.
                Dispatcher currentDispatcher = FromThread(Thread.CurrentThread);;
 
                // Auto-create the dispatcher if there is no dispatcher for
                // this thread (if we are allowed to).
                if(currentDispatcher == null)
                {
                    currentDispatcher = new Dispatcher();
                }
 
                return currentDispatcher;
            }
        }

我们可以new Thread ,然后执行获当前dispacher。

消息循环

        //<SecurityNote>
        // Critical - as this calls critical methods (GetMessage, TranslateMessage, DispatchMessage).
        // TreatAsSafe - as the critical method is not leaked out, and not controlled by external inputs.
        //</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe ]
        private void PushFrameImpl(DispatcherFrame frame)
        {
            SynchronizationContext oldSyncContext = null;
            SynchronizationContext newSyncContext = null;
            MSG msg = new MSG();
 
            _frameDepth++;
            try
            {
                // Change the CLR SynchronizationContext to be compatable with our Dispatcher.
                oldSyncContext = SynchronizationContext.Current;
                newSyncContext = new DispatcherSynchronizationContext(this);
                SynchronizationContext.SetSynchronizationContext(newSyncContext);
 
                try
                {
                    while(frame.Continue)
                    {
                        if (!GetMessage(ref msg, IntPtr.Zero, 0, 0))
                            break;
 
                        TranslateAndDispatchMessage(ref msg);
                    }
 
                    // If this was the last frame to exit after a quit, we
                    // can now dispose the dispatcher.
                    if(_frameDepth == 1)
                    {
                        if(_hasShutdownStarted)
                        {
                            ShutdownImpl();
                        }
                    }
                }
                finally
                {
                    // Restore the old SynchronizationContext.
                    SynchronizationContext.SetSynchronizationContext(oldSyncContext);
                }
            }
            finally
            {
                _frameDepth--;
                if(_frameDepth == 0)
                {
                    // We have exited all frames.
                    _exitAllFrames = false;
                }
            }
        }

如果普通的windwos 窗口程序,一般只有一个消息循环,消息循环每个线程必须要有一个。如果你创建windows不在UI线程,那么必须自己循环,不然程序跑不起来。

Appliaction 继承了 DispatcherObject,那么默认创建一个dispacker的对象。

      /// <summary>
        ///     Instantiate this object associated with the current Dispatcher.
        /// </summary>
        protected DispatcherObject()
        {
            _dispatcher = Dispatcher.CurrentDispatcher;
        }

可以退出,如果wpf起码有2个窗口,dispacher创建窗口。

wpf 程序会调用dispacher Run

我通过自己写c++ 工具搜索消息窗口(消息窗口不能被枚举函数枚举,所以你用spy++是无法查找到这个窗口,必须通过下面FindWindowEx函数才可以)

void find_hwnd_message(HWND hw){
	HWND child_hwnd = FindWindowEx(HWND_MESSAGE, hw, NULL, NULL);
	if(child_hwnd == NULL){
		return;
	}

	wchar_t class_name[256] = {0};
	GetClassName(hw, class_name, 255);
	wprintf_s(L"class_name: %s\n", class_name );
	find_hwnd_message(child_hwnd);
}

然后再main函数调用 find_hwnd_message (null); 这样子可以查询所有的消息窗口,你也可以填写class name 和 windows name 做过滤。

三、总结

application 启动时候就默认创建 dispacher,通过Dispacher 获取当前的,当前不存在就默认创建逻辑,然后启动主窗口,然后调用当前dispacher中Run ,用来消息循环,从而整个window 应用程序核心逻辑走完了。dispacher会绑定线程,会创建一个消息窗口。通过原理,我们可以推出来,dispacher是不能在很短时间做大量异步行为(如果消息还消耗时间的话),因为dispacher 消息处理不过来,阻塞界面。

四、资料

微软消息窗口文档介绍

c# 排查cpu高的函数和内存高的对象

一,背景

最近自己对c# 写的程序进行优化,我首先看代码,分析哪些代码是有问题,这种方式最枯燥,效率也不高,很难发现代码有问题。于是我发现c# 调试会有一个诊断工具,我们可以对内存快照和cpu使用率进行记录

二,逻辑

这种分析通过内存对象引用对象和占内存大小,进行分析,内存分析比较麻烦,必须自己对程序有比较多理解,结合逻辑分析。

CPU分析比较简单,通过时间段分析,哪些占用CPU比较高,然后对应分析就能很快分析出来。

三,例子

待补充,等以后专门记录分析。暂时记录一下,一个知识点记录。

自己也有别的内存泄漏分析工具,但实际效果不是那么好,虽然有一些帮助,这类解决分析,还是看应用和调用过程,如果引用的数目过高,如果不符合自己预期,基本就是有泄漏了。我觉得c#内存泄漏反而不是那么好排查,如果c++ 可能好一点,因为c#有GC,什么时候被释放这个是底层来实现的。c++ 我直接delete,我只要记录哪些是New的对象,然后过一段时间后,然后去检测没有被释放对象有哪些,再根据代码就比较容易找出来。

c# 打开浏览器商用代码

一,背景

这个需求看起来不复杂,但考虑通用话比较麻烦,一些用户还是用的win7,各种魔改版本。导致各种问题,通过一段时间测试,自己整理出来代码比较通用,可以处理各种用户情况。

二,代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Browser
{
    class BrowserHelperV2
    {
        public void Open(string url)
        {
            //微软edge最新浏览器 google浏览器 360极速版本 360安全浏览器 火狐浏览器 默认浏览器
            //循序自己调整,建议explorer 放在最后,最后用默认方式
            //其他浏览器的执行,可以自己添加
            //不一定成功,根据自己测试,只要注册表正常安装是没有什么问题的
            string[] browserNameList = { "msedge.exe", "chrome.exe", "360chrome.exe", "360se.exe", "firefox.exe" };
            processUrl(ref url);
            foreach (var name in browserNameList)
            {
                bool ret = OpenBrowser(url, name);
                if (ret)
                {
                    return;
                }
            }

            //默认浏览器
            OpenDefaultBrowser(url);
        }

        private bool OpenBrowser(string url, string name)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            try
            {
                startInfo.FileName = name;
                startInfo.Arguments = url;
                Process.Start(startInfo);
                return true;
            }
            catch (Exception e)
            {
                Debug.WriteLine("打开异常:" + e.Message);
            }

            return false;
        }

        private bool OpenDefaultBrowser(string url)
        {
            try
            {
                //要引入1.2的版本,网上单独下载,但微软官方没有找到有为什么不用1.0的
                Shell32.Shell shell = new Shell32.Shell();
                shell.Open(url);
                return true;
            }
            catch (Exception)
            {
                return OpenBrowser(url, "explorer.exe");
            }
        }

        /// <summary>
        /// 处理URL,防止解析默认浏览器无法解析格式导致问题
        /// </summary>
        /// <param name="url"></param>
        private void processUrl(ref string url)
        {
            if(url.Length > 5)
            {
                var begin_url = url.Substring(0, 5);
                if (!(begin_url.StartsWith("https") || begin_url.StartsWith("http")))
                {
                    url = "http://" + url;
                }
            }  
        }
    }
}

三,逻辑

先遍历主流的浏览器,一般情况是可以的,不需要完整路径,可以打开。如果打不开,就使用默认浏览器打开,我直接用window shell 打开的,这种比较通用,为什么不用explorer打开一个浏览器,因为他多开一个explorer,这样子感觉体验不是那么好。

c# mir2 法师诱惑之光的逻辑

  1. 判断诱惑对方是否为空
  2. 判断诱惑的怪物是否攻击自己,如果攻击自己目标自己,那么召唤不到这个宝宝,这个其实最影响诱惑成功率
  3. 如果已经被我诱惑就不做处理
  4. 最起码的50%成功和失败,如果然后设置等级*5+10 诱惑时间,如果超过这个时间就是白的,如果成功才走下面逻辑,这个时间就是愤怒时间,红色的。
  5. 怪物等级不能超过自己等级2级
  6. 怪物必须能诱惑
  7. 如果自身的等级太低,有1/5概率触发怪物愤怒,我们常见就是红色的名字
  8. 判断诱惑宝宝的个数是否超过限制
  9. 然后计算诱惑概率,目标的血量/100 ,如果值小于2就是等于2 ,如果大于2再乘以2 ,那么概率就是1/计算值,所以成功率不是高
  10. 设置宝宝的属性值:宝宝的最大的等级是 诱惑之光的等级*2 + 1, 所以宝宝最大等级7级

ShockTime: 诱惑的时间,被诱惑时间,黄色应该算是诱惑时间,诱惑的时间是不能移动和攻击人。红色是愤怒的时间 RageTime,一般是自己等级和技能等级太低,如果自己等级很高,技能等级很低也没有影响。

总结

最大的成功概率都是1/4,所以跟人物的魔法值没有关系,等级有一定关系。跟诱惑的方法没有关系,你诱惑的黄色的名字,然后再攻击,再诱惑其实也没有关系的


这个之前分析c# 传奇记录逻辑,虽然这个是传奇3,但逻辑跟国内应该差不多,国内的传奇(传奇2)都是deiphi 很久之前代码写的。c# 传奇3 和 国内的deiphi 源码搭建分享出来

网络字节序

背景

如果我们采用自定义二进制传输数据,那么我们就会遇到字节序的问题,我很早学习网络编程就遇到这个问题。

windows 系统一般提供字节本地字节转换网络字节,但我们看好多开源代码根本就没有用这个几个API,难道他们就不管字节序。最近我看了c# mir 传奇代码,然后找了一下资料总算明白了。

逻辑

本质服务器和客户端用同一套字节读写就能保证数据一致,假设c# 服务器和客户端都采用BinaryWriter BinaryReader

他这个是小端的。所以我们可以采用一套二进制的读写即可。同时我们也可以自己再封装二进制读写,直接本地字节转换网络字节序就可以了。说白只要服务器和客户端达成一致即可。

c# 在子线程然回调到主线程方法

背景

我们开发代码经常面对线程的问题,多线程性带来异步方便的同时也带来稳定性问题,所以我们需要解决掉线程问题

解决方案

1:加锁

这种方案基本是解决线程的基本方法,但要求开发者对代码非常清晰,不然很容易死锁。

2:投递回调或者事件到主线程或者同一个线程里面

基本原理尽可能在同一个线程里面,那么就没有线程竞争,这种实现方式基于主线程的队列,我们常常用主线程的消息队列,c# 貌似也是类似。只要事件队列加锁就可以了,那么加锁颗粒就非常少了,我们也可以自己创建一个线程,不停等待任务,然后处理返回数据,那么数据都在所谓的业务主线程。你可以看看很多开源代码基本这么写的,android 事件也类似。android每个线程都有一个时间循环队列,比喻我们请求网络数据后,直接投递数据到android ui的主线程就可以了,那么数据就没有竞争。

3:c#的方式

SynchronizationContext 这个每个线程都有

            sync_context.Post(new SendOrPostCallback((o) =>
            {
                //执行的代码
            }), null);

Dispatcher 这个对线所有控件都会继承这个,所以你必须要保留主要界面的 Dispatcher ,但我在开发时候,如果使用不当的话会导致界面很卡,大概是代码里面反复调用导致的问题。

c# md5 在某些电脑报错

此实现不是 Windows 平台 FIPS 验证的加密算法的一部分。我们能从网上能找到基本是这个错误,一个c# 系统API竟然会报错,然后根据网上修改APP.config关闭FIP算法是无效,不用再试了。然后修改注册表:

在window中打开功能里输入regedit,回车打开注册器。然后进入如下路径中

 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy   将enable设置为0 即可。

修改之后要重新启动电脑。

虽然上面这个可以解决问题,但用户电脑不可能要用户修改,后面通过从github找一个份MD5 实现代码来替换系统API的MD5,写这篇文章的意思,大家还是不要用c# md5,如果别人电脑开启fips就报异常,虽然这个配置默认关闭的,但可能个别用户是打开的。

c# wpf webview 屏蔽右键方法1

代码(直接copy)

 public class WebBrowserHostUIHandler : Native.IDocHostUIHandler
    {
        private const uint E_NOTIMPL = 0x80004001;
        private const uint S_OK = 0;
        private const uint S_FALSE = 1;

        public WebBrowserHostUIHandler(WebBrowser browser)
        {
            if (browser == null)
                throw new ArgumentNullException("browser");

            Browser = browser;
            browser.LoadCompleted += OnLoadCompleted;
            browser.Navigated += OnNavigated;
            IsWebBrowserContextMenuEnabled = true;
            Flags |= HostUIFlags.ENABLE_REDIRECT_NOTIFICATION;
        }

        public WebBrowser Browser { get; private set; }
        public HostUIFlags Flags { get; set; }
        public bool IsWebBrowserContextMenuEnabled { get; set; }
        public bool ScriptErrorsSuppressed { get; set; }

        private void OnNavigated(object sender, NavigationEventArgs e)
        {
            SetSilent(Browser, ScriptErrorsSuppressed);
        }

        private void OnLoadCompleted(object sender, NavigationEventArgs e)
        {
            Native.ICustomDoc doc = Browser.Document as Native.ICustomDoc;
            if (doc != null)
            {
                doc.SetUIHandler(this);
            }
        }

        uint Native.IDocHostUIHandler.ShowContextMenu(int dwID, Native.POINT pt, object pcmdtReserved, object pdispReserved)
        {
            return IsWebBrowserContextMenuEnabled ? S_FALSE : S_OK;
        }

        uint Native.IDocHostUIHandler.GetHostInfo(ref Native.DOCHOSTUIINFO info)
        {
            info.dwFlags = (int)Flags;
            info.dwDoubleClick = 0;
            return S_OK;
        }

        uint Native.IDocHostUIHandler.ShowUI(int dwID, object activeObject, object commandTarget, object frame, object doc)
        {
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.HideUI()
        {
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.UpdateUI()
        {
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.EnableModeless(bool fEnable)
        {
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.OnDocWindowActivate(bool fActivate)
        {
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.OnFrameWindowActivate(bool fActivate)
        {
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.ResizeBorder(Native.COMRECT rect, object doc, bool fFrameWindow)
        {
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.TranslateAccelerator(ref System.Windows.Forms.Message msg, ref Guid group, int nCmdID)
        {
            return S_FALSE;
        }

        uint Native.IDocHostUIHandler.GetOptionKeyPath(string[] pbstrKey, int dw)
        {
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.GetDropTarget(object pDropTarget, out object ppDropTarget)
        {
            ppDropTarget = null;
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.GetExternal(out object ppDispatch)
        {
            ppDispatch = Browser.ObjectForScripting;
            return S_OK;
        }

        uint Native.IDocHostUIHandler.TranslateUrl(int dwTranslate, string strURLIn, out string pstrURLOut)
        {
            pstrURLOut = null;
            return E_NOTIMPL;
        }

        uint Native.IDocHostUIHandler.FilterDataObject(IDataObject pDO, out IDataObject ppDORet)
        {
            ppDORet = null;
            return E_NOTIMPL;
        }

        public static void SetSilent(WebBrowser browser, bool silent)
        {
            Native.IOleServiceProvider sp = browser.Document as Native.IOleServiceProvider;
            if (sp != null)
            {
                Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
                Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");

                object webBrowser;
                sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out webBrowser);
                if (webBrowser != null)
                {
                    webBrowser.GetType().InvokeMember("Silent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty, null, webBrowser, new object[] { silent });
                }
            }
        }
    }

    internal static class Native
    {
        [ComImport, Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IDocHostUIHandler
        {
            [PreserveSig]
            uint ShowContextMenu(int dwID, POINT pt, [MarshalAs(UnmanagedType.Interface)] object pcmdtReserved, [MarshalAs(UnmanagedType.Interface)] object pdispReserved);

            [PreserveSig]
            uint GetHostInfo(ref DOCHOSTUIINFO info);

            [PreserveSig]
            uint ShowUI(int dwID, [MarshalAs(UnmanagedType.Interface)] object activeObject, [MarshalAs(UnmanagedType.Interface)] object commandTarget, [MarshalAs(UnmanagedType.Interface)] object frame, [MarshalAs(UnmanagedType.Interface)] object doc);

            [PreserveSig]
            uint HideUI();

            [PreserveSig]
            uint UpdateUI();

            [PreserveSig]
            uint EnableModeless(bool fEnable);

            [PreserveSig]
            uint OnDocWindowActivate(bool fActivate);

            [PreserveSig]
            uint OnFrameWindowActivate(bool fActivate);

            [PreserveSig]
            uint ResizeBorder(COMRECT rect, [MarshalAs(UnmanagedType.Interface)] object doc, bool fFrameWindow);

            [PreserveSig]
            uint TranslateAccelerator(ref System.Windows.Forms.Message msg, ref Guid group, int nCmdID);

            [PreserveSig]
            uint GetOptionKeyPath([Out, MarshalAs(UnmanagedType.LPArray)] string[] pbstrKey, int dw);

            [PreserveSig]
            uint GetDropTarget([In, MarshalAs(UnmanagedType.Interface)] object pDropTarget, [MarshalAs(UnmanagedType.Interface)] out object ppDropTarget);

            [PreserveSig]
            uint GetExternal([MarshalAs(UnmanagedType.IDispatch)] out object ppDispatch);

            [PreserveSig]
            uint TranslateUrl(int dwTranslate, [MarshalAs(UnmanagedType.LPWStr)] string strURLIn, [MarshalAs(UnmanagedType.LPWStr)] out string pstrURLOut);

            [PreserveSig]
            uint FilterDataObject(IDataObject pDO, out IDataObject ppDORet);
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct DOCHOSTUIINFO
        {
            public int cbSize;
            public int dwFlags;
            public int dwDoubleClick;
            public IntPtr dwReserved1;
            public IntPtr dwReserved2;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct COMRECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal class POINT
        {
            public int x;
            public int y;
        }

        [ComImport, Guid("3050F3F0-98B5-11CF-BB82-00AA00BDCE0B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface ICustomDoc
        {
            [PreserveSig]
            int SetUIHandler(IDocHostUIHandler pUIHandler);
        }

        [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IOleServiceProvider
        {
            [PreserveSig]
            uint QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
        }
    }

    [Flags]
    public enum HostUIFlags
    {
        DIALOG = 0x00000001,
        DISABLE_HELP_MENU = 0x00000002,
        NO3DBORDER = 0x00000004,
        SCROLL_NO = 0x00000008,
        DISABLE_SCRIPT_INACTIVE = 0x00000010,
        OPENNEWWIN = 0x00000020,
        DISABLE_OFFSCREEN = 0x00000040,
        FLAT_SCROLLBAR = 0x00000080,
        DIV_BLOCKDEFAULT = 0x00000100,
        ACTIVATE_CLIENTHIT_ONLY = 0x00000200,
        OVERRIDEBEHAVIORFACTORY = 0x00000400,
        CODEPAGELINKEDFONTS = 0x00000800,
        URL_ENCODING_DISABLE_UTF8 = 0x00001000,
        URL_ENCODING_ENABLE_UTF8 = 0x00002000,
        ENABLE_FORMS_AUTOCOMPLETE = 0x00004000,
        ENABLE_INPLACE_NAVIGATION = 0x00010000,
        IME_ENABLE_RECONVERSION = 0x00020000,
        THEME = 0x00040000,
        NOTHEME = 0x00080000,
        NOPICS = 0x00100000,
        NO3DOUTERBORDER = 0x00200000,
        DISABLE_EDIT_NS_FIXUP = 0x00400000,
        LOCAL_MACHINE_ACCESS_CHECK = 0x00800000,
        DISABLE_UNTRUSTEDPROTOCOL = 0x01000000,
        HOST_NAVIGATES = 0x02000000,
        ENABLE_REDIRECT_NOTIFICATION = 0x04000000,
        USE_WINDOWLESS_SELECTCONTROL = 0x08000000,
        USE_WINDOWED_SELECTCONTROL = 0x10000000,
        ENABLE_ACTIVEX_INACTIVATE_MODE = 0x20000000,
        DPI_AWARE = 0x40000000
    }

怎么使用?

private WebBrowserHostUIHandler _wbHandler;

_wbHandler = new WebBrowserHostUIHandler(WebViewControl);

WebViewControl就是对应的WebView类名,c++ 也有类似方法。其实不用太关心代码细节,这个都是com接口,微软有一些文档介绍,我记得以前我用c++找到过,写过类似的代码,这份代码是从老外那里直接复制,这样子就不需要用winform webview了。后面我会介绍用js的方法屏蔽右键

c#基于webview开发模块

背景

我们开发内容经常会变动,界面模块经常会增加功能,这个时候就可以考虑用webview来开发,比喻常见音乐播放器,QQ音乐,酷狗音乐,网易云音乐等等 PC端开发都是基于webview开发的,你用类似查看窗口工具,就可以看到他们的窗口类名,要么就是基于ie weview控件的,要么基于chrome webview控件。

c# 使用webview

直接添加webview控件即可,控件宽度和高度就看你需求了,自己稍微研究一下就可以了。

webview调用c#代码

我们肯定会用到js 调用c#代码,主要通过window.external 来访问c#方法,后面我在这块代码补充起来,现在写博客是在MAC,没法帖代码

可能遇到问题

  • c#定义的给JS调用,必须是PUBLIC,否则会调用异常
  • 传递类型基本是必须基本类型,如果是对象,那么考虑用JSON代替
  • 想c#调用JS代码,这个想法是错误的,因为会涉及到网页加载事件,那么c#就必须监听页面事件,这样子把业务复杂,耦合太多,那么直接设置好函数,然后直接在JS中主动调用函数,一样可以达到效果,反而代码更加流畅,更加优美。记住一点:c#提供接口,JS来调用。

总结

记得最开始在深圳上班第一家公司是做在线教育,那个时候PC项目采用c++ 加 webview 来做PC端,从无到有自己摸索出来。想不到现在又用这个技术实现云插件。所以知识这个东西,只要明白本质就可以了,知道方案,用什么语音实现并重要。多看书,多积累一些知识点是不会错的。

背景:

有的时候自己开发工具会被360误杀或者误报,所以就要告诉用户退出360或者添加信任。

原理;

检测360安全卫士和360杀毒的进程名字,进行比对。

`///

/// 360是否运行
///

///
public static bool IsRun360()
{
foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
{
if (p.ProcessName.ToLower() == “360tray”)
{
return true;
}

if (p.ProcessName.ToLower() == “360sd”)
{
return true;
}
}

return false;
}`