Tommy 碎碎念
https://blog.teatime.com.tw/
Tommy Wu's blog
tommy
2024-03-19T20:28:29Z
-
Windows console 模式的程式顯示 unicode 的方式
https://blog.teatime.com.tw/1/post/445
<p>原本有些小程式是使用 <a href="http://php.net/" target="_blank">php</a> 寫的 script 來跑, 不過... 由於 php 一直沒有支援 unicode (Windows wide string, 官方是說要等 PHP 6, 不過看起來不知道還要多久), 在處理 unicode 的檔案名稱時就會有問題, 所以... 還是需要自己寫些小程式來處理.</p><p>不過, 剛用 Visual C++ 寫的時候, 發現處理 unicode 時沒有什麼問題, 不過... 用 _tprintf (實際上應該就是 wprintf) 印出資料的時候, 都無法顯示出 unicode 的字.</p><p>找了相關的資料, 發現只要先去設定 stdout 的輸出模式, 就可以正常顯示 unicode 了. </p><pre class="c"><div class="insertcode"><pre class="c" style="font-family: monospace">_setmode<span class="br0">(</span>_fileno<span class="br0">(</span>stdout<span class="br0">)</span><span class="sy0">,</span> _O_U16TEXT<span class="br0">)</span><span class="sy0">;</span></pre></div></pre><p>只要程式先執行過上述的指令, 就可以正常的顯示 wide string (UTF16-LE) 的字串了. </p>
程式設計
VS2012
2013-05-05T10:27:31Z
tommy
-
讓 wscitecm.dll 支援 unicode
https://blog.teatime.com.tw/1/post/440
<p>不久前發現, 一直以來用來與 <a href="http://www.scintilla.org/SciTE.html" target="_blank">SciTE</a> 搭配使用的<a href="http://www.burgaud.com/scite-context-menu/" target="_blank"> wscitecm.dll</a> 在並無法處理 unicode 的路徑與檔名 (用了這麼多年才發現, 也算不容易....). </p><p>其實原本程式裡頭多數的寫法都有考量到 unicode 的問題, 所以實際改起來應該並不會太難 (不過由於改用 VS2012 來編譯, 所以 source code 也整理過了, 與原作者的版本有一些差異).</p><p>主要對於 unicode 檔名與路徑的支援與否的差異是出現在 <a href="http://msdn.microsoft.com/zh-tw/library/windows/desktop/bb776096%28v=vs.85%29.aspx" target="_blank">IContextMenu::InvokeCommand()</a> 這個函式的處理.</p><p>由於傳入的是一個指標, 就函式的定差來看, 是一個指向 <a href="http://msdn.microsoft.com/zh-tw/library/windows/desktop/bb773215%28v=vs.85%29.aspx" target="_blank">CMINVOKECOMMANDINFO</a> 這個結構的指標, 所以直接拿來使用時, 這個結構裡頭就只有包含 ANSI 的檔名與路徑, 自然無法做到 unicode 的支援. 而在 MSDN 上頭關於這個函式的說明中, 有提到,在 unicode 的環境, 那個指標實際上是一個指向 <a href="http://msdn.microsoft.com/zh-tw/library/windows/desktop/bb773217%28v=vs.85%29.aspx" target="_blank">CMINVOKECOMMANDINFOEX</a> 這個結構的指標, 裡頭除了原本的 ANSI 相關的檔名與路徑外, 還有額外儲存 unicode 版本的檔名與路徑.</p><p>我們只要先檢查 cbSize 的值, <strike>與 fMask 是否含有 CMIC_MASK_UNICODE</strike> (在某些情形下, 雖然大小不是 CMINVOKECOMMANDINFO, 但似乎 fMask 會是 0, 所以用這個來判斷反而會有問題),來判斷這個指標實際指向的結構, 是否有支援 unicode, 如果有, 就直接 cast 成 CMINVOKECOMMANDINFOEX 的指標, 就可以得到 unicode 的檔名與路徑, 這樣就能對 context menu 加上 unicode 的支援了.</p><p>如:</p><pre class="cpp"><div class="insertcode"><pre class="cpp" style="font-family: monospace">STDMETHODIMP CShellExt<span class="sy4">::</span><span class="me2">InvokeCommand</span><span class="br0">(</span>LPCMINVOKECOMMANDINFO lpcmi<span class="br0">)</span><br /><span class="br0">{</span><br /> HRESULT hr <span class="sy1">=</span> E_INVALIDARG<span class="sy4">;</span><br /> <br /> <span class="kw1">if</span> <span class="br0">(</span><span class="sy3">!</span>HIWORD<span class="br0">(</span>lpcmi<span class="sy2">-</span><span class="sy1">></span>lpVerb<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span><br /> UINT idCmd <span class="sy1">=</span> LOWORD<span class="br0">(</span>lpcmi<span class="sy2">-</span><span class="sy1">></span>lpVerb<span class="br0">)</span><span class="sy4">;</span><br /> <span class="kw1">switch</span><span class="br0">(</span>idCmd<span class="br0">)</span> <span class="br0">{</span><br /> <span class="kw1">case</span> <span class="nu0">0</span><span class="sy4">:</span><br /><span class="co2">#ifdef _UNICODE</span><br /> <span class="kw1">if</span> <span class="br0">(</span>lpcmi<span class="sy2">-</span><span class="sy1">></span>cbSize <span class="sy3">!</span><span class="sy1">=</span> <span class="kw3">sizeof</span><span class="br0">(</span>CMINVOKECOMMANDINFO<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span><br /> LPCMINVOKECOMMANDINFOEX lpcmiex <span class="sy1">=</span> <span class="br0">(</span>LPCMINVOKECOMMANDINFOEX<span class="br0">)</span>lpcmi<span class="sy4">;</span><br /> hr <span class="sy1">=</span> InvokeRealCommand<span class="br0">(</span>lpcmiex<span class="sy2">-</span><span class="sy1">></span>hwnd, lpcmiex<span class="sy2">-</span><span class="sy1">></span>lpDirectoryW, lpcmiex<span class="sy2">-</span><span class="sy1">></span>lpVerbW, lpcmiex<span class="sy2">-</span><span class="sy1">></span>lpParametersW, lpcmiex<span class="sy2">-</span><span class="sy1">></span>nShow<span class="br0">)</span><span class="sy4">;</span><br /> <span class="br0">}</span><br /> <span class="kw1">else</span> <span class="br0">{</span><br /> wchar_t dir<span class="br0">[</span>_MAX_DIR+1<span class="br0">]</span><span class="sy4">;</span><br /> wchar_t verb<span class="br0">[</span>1025<span class="br0">]</span><span class="sy4">;</span><br /> wchar_t param<span class="br0">[</span>1025<span class="br0">]</span><span class="sy4">;</span><br /> <br /> MultiByteToWideChar<span class="br0">(</span>CP_ACP, 0, lpcmi<span class="sy2">-</span><span class="sy1">></span>lpDirectory, -1, dir, _MAX_DIR<span class="br0">)</span><span class="sy4">;</span><br /> MultiByteToWideChar<span class="br0">(</span>CP_ACP, 0, lpcmi<span class="sy2">-</span><span class="sy1">></span>lpVerb, -1, verb, 1024<span class="br0">)</span><span class="sy4">;</span><br /> MultiByteToWideChar<span class="br0">(</span>CP_ACP, 0, lpcmi<span class="sy2">-</span><span class="sy1">></span>lpParameters, -1, param, 1024<span class="br0">)</span><span class="sy4">;</span><br /> <br /> hr <span class="sy1">=</span> InvokeRealCommand<span class="br0">(</span>lpcmi<span class="sy2">-</span><span class="sy1">></span>hwnd, dir, verb, param, lpcmi<span class="sy2">-</span><span class="sy1">></span>nShow<span class="br0">)</span><span class="sy4">;</span><br /> <span class="br0">}</span><br /><span class="co2">#else</span><br /> hr <span class="sy1">=</span> InvokeRealCommand<span class="br0">(</span>lpcmi<span class="sy2">-</span><span class="sy1">></span>hwnd, lpcmi<span class="sy2">-</span><span class="sy1">></span>lpDirectory, lpcmi<span class="sy2">-</span><span class="sy1">></span>lpVerb, lpcmi<span class="sy2">-</span><span class="sy1">></span>lpParameters, lpcmi<span class="sy2">-</span><span class="sy1">></span>nShow<span class="br0">)</span><span class="sy4">;</span><br /><span class="co2">#endif</span><br /> <span class="kw1">break</span><span class="sy4">;</span><br /> <span class="br0">}</span><br /> <span class="br0">}</span><br /> <span class="kw1">return</span> hr<span class="sy4">;</span><br /><span class="br0">}</span></pre></div></pre><br />當然除了這個地方外, 原本的程式有些字串並沒有加上 _T() 來處理, 型別也還是用 char, 不是 TCHAR, 把這些地方改一改後, 就會得到一個完整支援 unicode 的程式了.<p>產生的 dll 檔案與 source 都可以在這兒抓到: <a href="http://www.teatime.com.tw/~tommy/files/wscitecm_2.0.0.7z" target="_blank">http://www.teatime.com.tw/~tommy/files/wscitecm_2.0.0.7z</a> </p><p>安裝與移除的方法可以參考原作者的說明. 裡頭的 wscitecm.dll 是 x86 的版本 (Windows XP 或之後的版本應該都可以用). 而 wscitecm64.dll 是 x64 的版本 (應該是 Windows Vista 之後的版本都能用).</p><p>對於, 程式運作需要 msvcr110.dll, 如果你的系統沒有, 請裝上 <a href="http://www.microsoft.com/en-us/download/details.aspx?id=30679" target="_blank">Visual C++ Redistributable for Visual Studio 2012</a> 就應該可以用了. </p>
Software
VS2012
2013-02-25T11:03:34Z
tommy
-
Visual Studio 2012 在 Windows 8 底下無法使用 Shell32.Shell 的問題?
https://blog.teatime.com.tw/1/post/427
<p>最近開始用 C# 寫 .Net 的程式, 發現就寫程式的方便性來說, 比起直接用 C/C++ 寫要方便許多. 所以... 原本有些為了方便, 使用 php 來寫的 script, 也都順便改用 C# 來寫.</p><p>因為 Windows 8 與 Visual Studio 2012 出來後, 就由 MSDN 中抓回來用, 其實算很好用. 不過... 碰到一個原本在 Windows 7 搭配 Visual Studio 2010 時可以正常運作的程式, 改用 Windows 8 搭配 Visual Studio 2012 就會出現錯誤.</p><p>出錯的地方很簡單, 就類似這樣:</p><pre class="csharp"><div class="insertcode"><pre class="csharp" style="font-family: monospace"><span class="kw1">namespace</span> Shell32Test<br /><span class="br0">{</span><br /> <span class="kw4">class</span> Program<br /> <span class="br0">{</span><br /> <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">(</span><span class="kw4">string</span><span class="br0">[</span><span class="br0">]</span> args<span class="br0">)</span><br /> <span class="br0">{</span><br /> <span class="kw4">object</span> shell <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Shell32.<span class="me1">Shell</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span><br /> <span class="br0">}</span><br /> <span class="br0">}</span><br /><span class="br0">}</span></pre></div></pre><p>會出現下面的錯誤:</p><pre class="csharp"><div class="insertcode"><pre class="csharp" style="font-family: monospace">Unable to cast COM <span class="kw4">object</span> of type <span class="st0">'System.__ComObject'</span> to <span class="kw4">interface</span> type <span class="st0">'Shell32.Shell'</span>. <span class="kw1">This</span> operation failed because the QueryInterface call on the COM component <span class="kw1">for</span> the <span class="kw4">interface</span> with IID <span class="st0">'{286E6F1B-7113-4355-9562-96B7E9D64C54}'</span> failed due to the following error<span class="sy0">:</span> No such <span class="kw4">interface</span> supported <span class="br0">(</span>Exception from HRESULT<span class="sy0">:</span> 0x80004002 <span class="br0">(</span>E_NOINTERFACE<span class="br0">)</span><span class="br0">)</span>.</pre></div></pre><p>不過, 同一個程式, 在 Windows 7 下, 不管用 Visual Studio 2010 或 2012 都可以運作. 而且, 把那個可以運作的執行檔, 複製到 Windows 8 也一樣可以正常運作.</p><p>後來, 發現在 Windows 8 底下用 Visual Studio 2012 寫 Windows Form 的程式時, 也可以正常運作. 似乎只有在 console 程式會無法運作.</p><p>比較一下兩者的程式碼, 發現 Windows Form 的程式會在 Main 前面加上<span class="pln">一行:</span></p><pre class="csharp"><div class="insertcode"><pre class="csharp" style="font-family: monospace"><span class="br0">[</span>STAThread<span class="br0">]</span></pre></div></pre><p>試著加上變成:</p><pre class="csharp"><div class="insertcode"><pre class="csharp" style="font-family: monospace"><span class="kw1">namespace</span> Shell32Test<br /><span class="br0">{<br /> [STAThread]<br /></span> <span class="kw4">class</span> Program<br /> <span class="br0">{</span><br /> <span class="kw1">static</span> <span class="kw1">void</span> Main<span class="br0">(</span><span class="kw4">string</span><span class="br0">[</span><span class="br0">]</span> args<span class="br0">)</span><br /> <span class="br0">{</span><br /> <span class="kw4">object</span> shell <span class="sy0">=</span> <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span class="kw3">new</span></a> Shell32.<span class="me1">Shell</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span><br /> <span class="br0">}</span><br /> <span class="br0">}</span><br /><span class="br0">}</span></pre></div></pre><p>這樣子居然可以正常執行了. 真是個奇怪的 bug. </p>
程式設計
C#
VS2012
Windows 8
.Net
2012-09-19T17:32:58Z
tommy