主頁 > .NET開發 > 撰寫一個Open Live Writer的VSCode代碼插件

撰寫一個Open Live Writer的VSCode代碼插件

2020-10-05 23:24:33 .NET開發

起因

又是一年多沒有更新過博客了,最近用Arduino做了一點有意思的東西,準備寫一篇博客,打開塵封許久的博客園,發現因為Windows Live Writer停止更新,博客園推薦的客戶端變為了Open Live Writer(基于Windows Live Writer代碼,然而GitHub上的代碼已經快一年沒更新了),OK,安裝之后開始寫作,寫著寫著就發現問題了:在準備插入代碼的時候發現沒有對應的Open Live Writer代碼插件,

兩個編輯器

在日常編輯Arduino代碼時我會用到兩個編輯器,Arduino IDE和Visual Studio Code(以下簡稱VSCode),

Arduino IDE語法高亮效果如下:

Arduino IDE語法高亮

VSCode中語法高亮效果如下:

VSCode語法高亮

可以看到,Arduino IDE 語法高亮明顯不如VSCode的豐富,更重要的是,Arduino IDE沒有代碼智能提示,這年頭,寫代碼沒有智能提示就少了半條命,所以,在需要大量寫Arduino代碼的時候,我都是使用VSCode完成,附帶說一句,VSCode支持Arduino可以參考這篇文章:Enabling Arduino Intellisense with Visual Studio Code),

Arduino IDE 復制成HTML格式

在Arduino IDE中有一個功能叫做“復制為HTML格式”,上述語法高亮函式簽名復制出來的HTML代碼如下所示:

<pre>
<font color="#00979c">void</font> <font color="#000000">Matrix4</font><font color="#434f54">:</font><font color="#434f54">:</font><font color="#d35400">writeSprite</font><font color="#000000">(</font><font color="#00979c">int</font> <font color="#000000">startColumn</font><font color="#434f54">,</font> <font color="#00979c">int</font> <font color="#000000">startRow</font><font color="#434f54">,</font> <font color="#00979c">const</font> <font color="#00979c">byte</font> <font color="#434f54">*</font><font color="#000000">sprite</font><font color="#000000">)</font>

</pre>

可以看到,其中使用了不被推薦的font標簽,

如果喜歡Arduino IDE的語法高亮的話,在寫博客的時候可以將Open Live Writer切換到Source模式,并將要復制的HTML代碼粘貼到要插入的位置,但是這樣要麻煩一點,會在Edit模式和Source模式之間切換,

VSCode代碼編輯插件

然而在網上并沒有找到VSCode對應的代碼編輯插件,于是將要分享的東西放在一旁,開始了折騰,

嘗試方案一:VSPaste

一見VSCode,立刻想到VS,立刻想到VSPaste(Windows Live Writer上插入VS代碼的插件),上面那句話是模仿魯迅的,原話是:一見短袖子,立刻想到白臂膊,立刻想到全裸體,,,

VSCode和VS都是微軟的,我就想VSPaste應該也支持VSCode吧,VSPaste我熟啊,當扯訓專門寫了一篇文章:一次查找Windows Live Writer的VSPaste插件丟失RTF格式資訊的經歷,

第一次嘗試失敗

于是找到Open Live Writer的安裝目錄,尋找安裝目錄有多種方式,包括但不限于:利用任務管理器查找,利用Everything查找,通過開始選單快捷方式查找,我輕車熟路的在安裝目錄下面新建一個Plugins目錄,將VSPaste.dll復制過去,然后信心滿滿的重啟Open Live Writer,納尼,啥也沒有!

第二次嘗試失敗

博客園推薦的,應該有對應的官方插件,我去找找看官方對應的Windows Live Writer和Open Live Writer有什么區別,發現一篇文章:OpenLiveWriter代碼插件 ,

通過對文章的閱讀,明白了不生效的原因在于介面不匹配,那么這個問題就好解決了,反編譯VSPaste的源代碼,新建工程,將介面改成新的介面,

就在我準備自己動手的時候,無意在GitHub上發現了一個專案:LiveWriter.VSPaste,該專案將VSPaste移植到了Open Live Writer(下文簡稱OLW,實在是懶得打字了),

于是興沖沖的下載了對應的檔案,將其復制到Plugins目錄,然后信心滿滿的重啟OLW,然而,這次還是啥都沒有,

第三次嘗試失敗

為了一探上次加載插件失敗的原因,我下載了LiveWriter.VSPaste的原始碼,將生成的檔案復制過去,奇怪的是無論是Debug還是Release,除了顯示找不到圖示之外,插件能夠正確加載,

缺少圖示

更神奇的是,將下載的檔案用ILSpy進行反編譯,和源代碼比較,沒有發現明顯差異,這可真是嗶了狗了,同時也勾起了我的好奇心,于是我下載了OLW的原始碼,進行編譯生成,這次倒是比較順利,無風無浪,一氣呵成,

設定好啟動專案,找到生成目錄,將下載的檔案復制到Plugins目錄下,正式開始查找原因,在解決方案管理器中查找Plugin,發現有個PluginLoader檔案,發現里面有個叫LoadPluginsFromDirectory的函式,直覺告訴我應該就是它,打上斷點,開始除錯,果然,斷點中斷了,經過單步除錯一步步的到了案發現場:一個名叫LoadFromWithRetry的函式,

LoadFromWithRetry

修改LoadFromWithRetry函式的代碼,獲取例外詳情,得到了以下例外資訊:An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.

看樣子是從網路加載程式集,可是也不對啊,下載的檔案我用ILSpy看過啊,沒有從網路下載啊,真是頭大,問題一時陷入了僵局,

第四次嘗試失敗

就在我一籌莫展的時候,無意中我在點開下載檔案的屬性中發現了這么一個東西:

來自其他計算機

勾選解除鎖定,點擊確定,再次啟動除錯,果然沒有在LoadFromWithRetry函式的例外處理處中斷,真是山重水復疑無蹤,柳暗花明又一村,

還沒等我好好高興,又跳出來一個報錯對話框:

缺少圖示Aseertion

接下來又是前面缺少圖示的報錯,定位到剛剛報錯的行:

VeriyPluginBitmap

通過分析代碼中Bitmap的構造:

BitmapConstructor

我們可以確定,加載圖片的路徑是程式集名稱加上匯出設定中的ImagePath,通過ILSpy反編譯生成的檔案,查看程式集資訊,可以發現其中并沒有任何資源,

程式集資訊

打開工程檔案,查看對應圖片的屬性,發現其生成操作為無,將其改為嵌入的資源,

圖示生成屬性

重新生成、復制檔案、開始除錯,一切順利完成,正確顯示了插件,

正確顯示插件

終究是無用

再次信心滿滿,打開VSCode,復制代碼,點擊OLW中的VSPaste,然而,視圖中還是啥都沒有,

經過之前上次的折騰(一次查找Windows Live Writer的VSPaste插件丟失RTF格式資訊的經歷),我的第一反應就是判斷剪貼板中是否含有RTF格式的資料,因為VSPaste的原理是判斷剪貼板中是否存在RTF格式的資料,如果存在,將其轉換為HTML,

新建一個WinForm工程,使用Clipboard.ContainsData(DataFormats.Rtf) 判斷剪貼板中是否含有RTF格式資料,嗯,果然沒有,此路不通,罷了罷了,

嘗試方案二:尋找已有插件

尋找資料

在搜索引擎中搜索VSCode Open Live Writer,沒有發現現有的插件,想到Arduino IDE都有復制成HTML的功能,在網上搜搜看有沒有VSCode復制成HTML的資料,于是發現了這篇文章:Copy As HTML From VSCode,

設定VSCode

在復制代碼之間,需要設定VSCode,具體來講的話就是打開編輯器的Copy With Syntax Highlighting功能,具體可參考Copy As HTML From VSCode,

學習借鑒

經過閱讀Copy As HTML From VSCode中的代碼,基本理解了代碼的思路,大佬寫的功能比較全面,包含行號顯示,還有一些如檔案名、展開折疊等的可選項,可選項如下:

在使用程序中,也發現了一個小bug,具體來講,就是VSCode的代碼中存在換行符時,生成的html代碼中會出現br后接著div的情況,導致空行前后的行會粘連在一起,進而導致代碼比行號更高,

代碼高于行號

艱難的決定

這個是基于html和js的,我不想每次插入代碼都經過瀏覽器打開網頁進行處理,再復制回OLW的源代碼中,而且功能豐富,我也用不了那么多,所以最終決定,還是自己寫一個VSCode代碼插件,實作在OLW中點擊就可插入的功能,

嘗試方案三:自己撰寫插件

新建專案

說動手就動手,第一步當然是新建專案,如下:

新建一個類別庫專案,命名為VSCodePaste,并在其中添加OpenLiveWriter.Api的參考,

OpenLiveWriter.Api.dll位于OLW安裝目錄下

添加一個VSCodePaste類,并設定好相應的特性,

除了GUID和PublisherUrl外,其他元資料我都是從LiveWriter.VSPaste中修改的,

    [InsertableContentSource("Paste from Visual Studio Code", SidebarText = "from Visual Studio Code"),
     WriterPlugin("{590ea9a7-b922-4de6-a712-b0ce6499cebd}", "VSCodePaste",
         Description = "Easily transfer syntax highlighted source code from Visual Studio Code to elegant HTML in Open Live Writer.",
         PublisherUrl = "https://www.cnblogs.com/yiyan127", ImagePath = "icon.png")]

將VSCodePaste繼承自ContentSource,并重寫CreateContent方法

        public override DialogResult CreateContent(IWin32Window dialogOwner, ref string newContent)
        {
            return DialogResult.Cancel;
        }

此時僅僅是回傳一個值,

將要作為圖示的檔案復制進入專案中,并在屬性頁中將生成的操作設定成嵌入的資源,

我是利用everything在VSCode的安裝目錄下的圖示中尋找的VSCodeIcon

第一次生成錯誤

根據錯誤詳細資訊可以得知,是專案Framework版本號低于OpenLiveWriter.Api.dll的Framework版本號,將專案版本號改成4.6.1即可(更高也行),

第一次生成錯誤

圖示又找不到

圖示的名稱是VSCodePaste.icon.png,這個名稱是參考LiveWriter.VSPaste的VSPaste.icon.png,但是在將生成檔案復制到OLW的Plugin目錄下后,重新打開OLW,又在報插件圖示找不到,

打開ILSpy,查看程式集,發現在資源中圖示名為VSCodePaste.VSCodePaste.icon.png,而在LiveWriter.VSPaste中,圖示名為VSCode.icon.png,真是奇怪,屬性中的設定都完全一樣,那么問題出在哪里呢,一定有某個地方不一樣,

在網上查詢內嵌的資源,查不到什么干貨,換成EmbeddedResource使得Bing搜索,發現有篇文章講得比較清楚:Understanding Embedded Resources in Visual Studio .NET,參考原文如下:EmbedResourcesName

查看LiveWriter.VSPaste的默認命名空間,果然為空,于是原因就知道了:

  • 專案中檔案名稱為VSCodePaste.icon.png
  • VS在生成時,自動在檔案名稱前加上默認的命名空間VSCodePaste,于是在生成的程式集中資源名稱變成了VSCodePaste.VSCodePaste.icon.png
  • Bitmap建構式查找的是VSCodePaste.icon.png,而程式集中是不存在對應名稱的資源的,問題就發生了,

于是解決方案也就有了,要么將默認命名空間改為空,要么將檔案改為icon.png,我當然是選第二個,

在VS中,當默認命名空間不為空時,是不能將默認命名空間修改為空,除非卸載專案直接改工程檔案,猜測LiveWriter.VSPaste中將默認命名空間設為空是為了鏈接檔案,避免復制圖示,

HTML不一致

繼續進行撰寫插件,很快就發現了第一個問題:在工程中使用Clipboard.GetData(DataFormats.Html)獲取的Html和Copy As HTML From VSCode在js中的onpaste事件獲取的Html不一致,

在js中獲取的Html如下所示,可以很明顯的看出來是一個Html檔案:

onpasteHtml

在WinForm工程中獲取的Html如下,可以看到,除了Html檔案以外,在前面還多了一些奇奇怪怪的字符:

WinFormHtml

從圖中看,感覺前面一組:分隔的鍵值對挺像是對Html的描述,于是查找相關資料以確認,找到了HTML Clipboard Format驗證了猜想,還找到了Add HTML code to the clipboard by using Visual C++,其中說明了使用VC++設定Html代碼到剪貼板中,所以猜測,是瀏覽器作了相應的處理,將描述給去掉了,

提取HTML片段

既然瀏覽器可以處理,那我們自己也可以處理,而且可以處理的更徹底一點,直接提取出Fragment中的內容,因為有意義的只有Fragment中的內容,其外層總是body和html,

從HTML Clipboard Format中可以得出,只要我們獲取到StartFragment和EndFragment的偏移,直接求子串就可以了,代碼如下:

        private static readonly Regex DescriptionRegex = new Regex(@"^([a-zA-Z]+:[a-zA-Z0-9\.]+\r\n)+");

        internal static string Extract(string html)
        {
            var matches = DescriptionRegex.Matches(html);
            if (matches.Count == 0)
            {
                return string.Empty;
            }

            int startIndex = -1;
            int endIndex = -1;
            var descriptions = matches[0].Value.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var description in descriptions)
            {
                var pairs = description.Split(':');
                var key = pairs[0];
                var value = pairs[1];

                if (key == "StartFragment")
                {
                    startIndex = int.Parse(value);
                    continue;
                }

                if (key == "EndFragment")
                {
                    endIndex = int.Parse(value);
                    continue;
                }
            }

            if (startIndex == -1 || endIndex == -1)
            {
                return string.Empty;
            }

            return html.Substring(startIndex, endIndex - startIndex);
        }

代碼很簡單,利用正則運算式提取出描述部分,再查找片段的開始和結束,然后提取子串,

就此止步還是進一步前進

提取出子串了,貌似也能湊和使用了,畢竟保持了基本的樣式,然而在VSPaste及博客園生成的代碼中,是包含在pre標簽中的,我想了想,還是保持一致吧,把代碼放在pre標簽中,中間用span來標明樣式,

接下來的問題,是直接將片段代碼轉換成pre樣式么?想了想,還是決定加一個中間層,原因如下:

  • 直接轉換代碼不可避免的會保存層次資訊,因為HTML片段本身就是一個層次結構
  • 將資料和表現分離開,代碼片段和pre標簽都可以視為同樣資料的不同表現,而且pre標簽的表現方式也可能變化,

中間層就選用XElement,因為HTML也可以視作一種不規范的XML,

轉換成XElement

轉換的要點就在于識別出HTML元素(標簽)的結構,即屬性、內部文本及何時開始、何時結束,好在我們是針對VSCode代碼的這種專有結構,所以邏輯可以寫得比較簡單,

層次結構處理

層次結構可以使用Stack(堆疊)來表示,其處理包括以下情況:

  • 標簽開始:標簽元素添加至堆疊頂元素并入堆疊
  • 標簽結束前:將內部文本添加至堆疊頂元素
  • 標簽結束:標簽元素出堆疊
  • 標簽開始即結束(特殊標簽,如br):標簽元素添加至堆疊頂元素(無需出入堆疊)

層次結構的處理代碼如下:

        private void BeginTag(Stack<XElement> tagStack, string tagInfo)
        {
            var matches = TagStyleRegex.Matches(tagInfo);
            Contract.Assert(matches.Count == 1);

            var tagName = matches[0].Groups["tagName"].Value;
            var style = matches[0].Groups["style"].Value; //while no style,the value is string.Empty;

            var tagElement = new XElement(tagName, new XAttribute("style", style));
            if (tagStack.Count == 0)
            {
                _rootElement = tagElement;
            }
            else
            {
                tagStack.Peek().Add(tagElement);
            }
            tagStack.Push(tagElement);
        }

        private void AppendTag(Stack<XElement> tagStack, string tagInfo)
        {
            var tagElement = new XElement(tagInfo);
            tagStack.Peek().Add(tagElement);
        }

        private void EndTag(Stack<XElement> tagStack, string tagInfo)
        {
            var tagElement = tagStack.Peek();
            Contract.Assert(tagElement.Name == tagInfo);
            tagStack.Pop();
        }

        private void AppendTagText(Stack<XElement> tagStack, string text)
        {
            var tagElement = tagStack.Peek();
            if (!string.IsNullOrEmpty(text))
            {
                tagElement.Add(new XText(text));
            }
        }

其中使用的正則運算式如下:

private static readonly Regex TagStyleRegex = new Regex(@"^(?<tagName>[a-zA-Z]+)(\s+style=""(?<style>.+)"")?");

正則運算式中的style部分是可選的,當沒有style時,通過Groups["style"].Value獲取到的值為空,

對應的文本處理

  • 遇到字符<:標識著標簽塊的開始或即將結束,如下一個字符是/,則標識著標簽即將結束,否則標志著標簽的開始,如果標簽即將結束,需要將緩沖區中的內部文本添加至堆疊頂元素,
  • 遇到字符>:標識著標簽塊的宣告完成(即標簽開始)或正式結束,需要配合遇到字符<時的情況做處理,對應著標簽開始、標簽結束、標簽開始即結束這三種情況,
  • 將字符添加到緩沖區

文本處理的代碼如下:

        private void ParseFragment(TextReader reader)
        {
            bool isTagEnd = false;
            Stack<XElement> tagStack = new Stack<XElement>();
            StringBuilder sb = new StringBuilder();

            int num = reader.Read();
            while (num != -1)
            {
                char c = (char)num;
                switch (c)
                {
                    case '<':
                        {
                            num = reader.Read();
                            Contract.Assert(num != -1);
                            c = (char)num;
                            if (c == '/')
                            {
                                AppendTagText(tagStack, sb.ToString());
                                sb.Clear();
                                isTagEnd = true;
                            }
                            else
                            {
                                sb.Append(c);
                            }
                            break;
                        }
                    case '>':
                        {
                            if (isTagEnd)
                            {
                                EndTag(tagStack, sb.ToString());
                                sb.Clear();
                            }
                            else
                            {
                                var tagInfo = sb.ToString();
                                if (tagInfo == "br")
                                {
                                    AppendTag(tagStack, tagInfo);
                                }
                                else
                                {
                                    BeginTag(tagStack, tagInfo);
                                }
                                sb.Clear();
                            }

                            isTagEnd = false;
                            break;
                        }
                    default:
                        sb.Append(c);
                        break;
                }
                num = reader.Read();
            }
        }

將XElement轉換成pre

三層結構

這部分主要就是將XElement的層次結構生成代碼,在VSCode的代碼中包括三層:

  • 第一層的div是全域樣式,包括背景和字體設定,
  • 第二層的div和br,代表第一行
  • 第三層的span,代表行中的一部分,即存在相同顏色的文本

代碼如下:

        public static string Convert(XElement rootElement)
        {
            using (var writer = new StringWriter())
            {
                writer.Write(string.Format("<pre class=\"code\" {0}>", rootElement.Attribute("style")));
                ConvertFragment(rootElement, writer);
                writer.Write("</pre>");
                return writer.ToString();
            }
        }

        private static void ConvertFragment(XElement rootElement, TextWriter writer)
        {
            foreach (var lineElement in rootElement.Elements()) //discard the root div which contains style
            {
                ConvertLine(lineElement, writer);
                writer.Write(Environment.NewLine);
            }
        }

        private static void ConvertLine(XElement lineElement, TextWriter writer)
        {
            foreach (var partElement in lineElement.Elements())
            {
                if (partElement.Name == "span")
                {
                    ConvertSpan(partElement, writer);
                }
            }
        }

span的處理

值得一提的是針對span的處理,根據包含的空格數量,可以分為三種:

  • 不包含空格:原樣添加,
  • 全為空格:添加同樣數量的空格,
  • 部分為空格:添加文本前空格、在span部分中去除空格并添加span部分、添加文本后空格,

代碼如下:

        private static void ConvertSpan(XElement spanElement, TextWriter writer)
        {
            var value = spanElement.Value;
            var replaced = value.Replace("&#160;", " ");

            int startSpaceCount = 0;
            for (int i = 0; i < replaced.Length; i++)
            {
                if (replaced[i] == ' ')
                {
                    writer.Write(' '); //write 
                    startSpaceCount++;
                }
                else
                {
                    break;
                }
            }

            //&#160; length is 6 and space length is 1,if one is replaced,length will minus 5 
            int endSpaceCount = (value.Length - replaced.Length) / 5 - startSpaceCount;

            if (startSpaceCount == 0 && endSpaceCount == 0)
            {
                writer.Write(spanElement.ToString());
            }
            else
            {
                if (startSpaceCount != replaced.Length) //there will be other text
                {
                    writer.Write(string.Format("<span style=\"{0}\">{1}</span>",
                        spanElement.Attribute("style"),
                        replaced.Trim()));
                    for (int i = 0; i < endSpaceCount; i++)
                    {
                        writer.Write(" ");
                    }
                }
            }
        }

其中計算結束空格字符數量使用了一個技巧:結束空格字符數量=(替換前字符長度-替換后字符數量)/ 5-開始空格字符數量,其原理如下:

  • 結束空格字符數量=總的空格數量-開始空格字符數量,
  • 總的空格數量=(替換前字符長度-替換后字符數量)/ 5,即每替換一個空格,其長度從6(&#160的長度)變成了1( 的長度),總共減少了5個字符,

樣式示范

void Matrix4::writeSprite(int startColumn, int startRow, const byte *sprite)
{
  int columnCount = sprite[0];
  int rowCount = sprite[1];

  if (rowCount == 8 && startRow == 0)
  {
    for (int i = 0; i < columnCount; i++)
    {
      int c = startColumn + i;
      if (c >= 0 && c < 80)
        setColumn(c, sprite[i + 2]);
    }
  }
  else
  {
    for (int i = 0; i < columnCount; i++)
      for (int j = 0; j < rowCount; j++)
      {
        int c = startColumn + i;
        int r = startRow + j;
        if (c >= 0 && c < 80 && r >= 0 && r < 8)
          setDot(c, r, bitRead(sprite[i + 2], j));
      }
  }
}

完整代碼

博客園:VSCodePaste

彩蛋

VSCode的Copy With Syntax Highlighting開關

啟用前

啟用后

通過比較兩個圖可以得知,啟用開關之后剪貼板中支持的格式多了HTML

VS的Copy As Html

在寫博客插入VS代碼時,隨便研究了一下VS復制的Html剪貼板格式,可以發現,和我們生成的樣式很接近,

VSHtml

所以,其實也可以不用VSPaste,自己寫一個VSHtmlPaste,事實上,我也寫了一個,感興趣的請參考前面的代碼鏈接,在寫的時候出現了一個問題,就是VS產生的Html中的EndHTML、EndFragment、EndSelection比實際的多了6,這個問題就不在這里追究了,不然真成了老太太的裹腳布,

補充一下:VS的Copy As Html功能需要安裝插件,我的是VS2019,安裝了Productivity Power Tools 2017/2019,

參考鏈接

Enabling Arduino Intellisense with Visual Studio Code

一次查找Windows Live Writer的VSPaste插件丟失RTF格式資訊的經歷

OpenLiveWriter代碼插件

LiveWriter.VSPaste

GitHub - OpenLiveWriter/OpenLiveWriter: An open source fork of Windows Live Writer

Copy As HTML From VSCode

HTML Clipboard Format

Add HTML code to the clipboard by using Visual C++

Understanding Embedded Resources in Visual Studio .NET

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/158243.html

標籤:.NET技术

上一篇:IdentityServer4系列 | 初識基礎知識點

下一篇:撰寫一個Open Live Writer的VSCode代碼插件

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more