這裏顯示兩個版本的差異處。
Both sides previous revision 前次修改 下次修改 | 前次修改 | ||
programming:dot_net_framework [2013/07/19 16:51] wenpei |
programming:dot_net_framework [2013/07/29 16:42] (目前版本) wenpei |
||
---|---|---|---|
行 16: | 行 16: | ||
==== Create Project ==== | ==== Create Project ==== | ||
首先建立新的 Solution,選擇 Visual C++ → CLR → Class Library | 首先建立新的 Solution,選擇 Visual C++ → CLR → Class Library | ||
+ | |||
+ | {{:programming:createclrproject.png?nolink&640|}} | ||
接著建立 C# 的 Console Project,在 Solution Explorer 視窗裡面的 Solution 'CLRTest' 上按滑鼠右鍵 → Add → New Project | 接著建立 C# 的 Console Project,在 Solution Explorer 視窗裡面的 Solution 'CLRTest' 上按滑鼠右鍵 → Add → New Project | ||
- | 完成後 Solution Explorer 應該會長這樣 | + | {{:programming:createuiproject.png?nolink&640|}} |
+ | |||
+ | 這邊 UI 的部分看是要選 Console Application 或是 Windows Forms Application 都可以。 | ||
接著在 C# 的 Project 上按滑鼠右鍵 -> Add Reference | 接著在 C# 的 Project 上按滑鼠右鍵 -> Add Reference | ||
+ | |||
+ | {{:programming:solutionexplorer_addref.png?nolink|}} | ||
選擇 Project 分頁,然後選擇剛剛建立的 CLR Project | 選擇 Project 分頁,然後選擇剛剛建立的 CLR Project | ||
+ | |||
+ | {{:programming:addrefproject.png?nolink|}} | ||
完成後應該就可以在 C# Project 的 References 裡面看到 CLR 的 Project | 完成後應該就可以在 C# Project 的 References 裡面看到 CLR 的 Project | ||
+ | |||
+ | {{:programming:addref_finished.png?nolink|}} | ||
==== CLR Class Library ==== | ==== CLR Class Library ==== | ||
- | 在完成 Project 的建立之後,就可以開始寫 CLR Class 裡面所需要的功能,像是把 COM DLL 讀進來用,或是寫只有在 C++ 才能用的 Windows Imaging Component (WIC) 等等。 | + | 在完成 Project 的建立之後,就可以開始寫 CLR Class 裡面所需要的功能,像是把 COM DLL 讀進來用,或是寫只有在 C++ 才能用的 [[http://msdn.microsoft.com/en-us/library/windows/desktop/ee719902%28v=vs.85%29.aspx|Windows Imaging Component (WIC)]] 等等。 |
這邊範例先增加兩個簡單的功能:Add、Minus | 這邊範例先增加兩個簡單的功能:Add、Minus | ||
行 73: | 行 83: | ||
這時用 Object Browser 打開 CLR Project,應該可以看到 Class 裡面有兩個 function 可以使用。 | 這時用 Object Browser 打開 CLR Project,應該可以看到 Class 裡面有兩個 function 可以使用。 | ||
+ | |||
+ | {{:programming:objbrowser.png?nolink|}} | ||
==== C# ==== | ==== C# ==== | ||
行 96: | 行 108: | ||
</code> | </code> | ||
+ | 若編譯錯誤,可能是平台問題,打開 C# Project 的 Properties,在 Build Tab 裡面的 Platform target 選擇 x86 | ||
+ | |||
+ | {{:programming:clr_x86.png|}} | ||
+ | |||
+ | ==== 進階應用 ==== | ||
+ | CLR 的特色之一就是可以直接拿 .Net Framework 的東西來用,因此要和 C# 互通特別容易,像是在 CLR 層將一張圖片檔案透過 GDI+ 打開,接著轉成 Byte Array 給 COM DLL 使用。 | ||
+ | |||
+ | 例如,COM DLL 內定義一個 buffer pointer 為 | ||
+ | BYTE *pBuffer; | ||
+ | |||
+ | 在 CLR 層定義需要用到的類別 | ||
+ | |||
+ | <code> | ||
+ | #using <system.drawing.dll> | ||
+ | using namespace System; | ||
+ | using namespace System::Drawing; | ||
+ | using namespace System::Drawing::Imaging; | ||
+ | </code> | ||
+ | |||
+ | 將放在 path 的圖片打開: | ||
+ | |||
+ | Bitmap^ srcBitmap = gcnew Bitmap(path); | ||
+ | |||
+ | 透過 Bitmap.LockBits() 固定住記憶體位址,創建一塊 array<Byte> 記憶體空間,並將 Bitmap 的內容複製過去,最後用 pin_ptr 取得 pointer address,這樣就可以指定給 pBuffer 了!並且在 LockBits 使用完之後,記得要 UnlockBits。 | ||
+ | |||
+ | <code> | ||
+ | BitmapData^ srcBitmapData = srcBitmap->LockBits(System::Drawing::Rectangle(0, 0, srcBitmap->Width, srcBitmap->Height), ImageLockMode::ReadWrite, PixelFormat::Format32bppArgb); | ||
+ | IntPtr ptr = srcBitmapData->Scan0; | ||
+ | int bytes = Math::Abs(srcBitmapData->Stride) * srcBitmapData->Height; | ||
+ | array<Byte>^ rgbValues = gcnew array<Byte>(bytes); | ||
+ | System::Runtime::InteropServices::Marshal::Copy(ptr, rgbValues, 0, bytes); | ||
+ | pin_ptr<Byte> p = &rgbValues[0]; | ||
+ | Byte* np = p; | ||
+ | pBuffer = np; | ||
+ | |||
+ | // ...... | ||
+ | |||
+ | srcBitmap->UnlockBits(srcBitmapData); | ||
+ | </code> | ||
+ | |||
+ | 另外一個應用就是把 UI 建立好的 Bitmap 傳到 wrapper 層,轉換成 array<Byte> 後,再傳給 COM DLL 去把圖片放進去,這樣就可以直接把底層修改好的圖片顯示在 UI 上。 | ||
+ | |||
+ | CLR 層的寫法是透過 Bitmap^% 取得一個 Bitmap 的 handle,並先產生好一塊 Byte Array 空間,COM DLL 修改完 Byte Array 後,複製到傳入的 Bitmap Handle | ||
+ | |||
+ | 關於 **^%** 的用法可參考 [[http://stackoverflow.com/questions/6616599/does-the-symbol-replace-cs-ref-in-parameter-passing-in-c-cli-code|Does the ^ symbol replace C#'s “ref” in parameter passing in C++/CLI code?]] | ||
+ | |||
+ | <code> | ||
+ | void CLRTestClass::SetBitmap(int temperature, int tint, Bitmap^% outputBitmap) | ||
+ | { | ||
+ | BitmapData^ oBitmapData = outputBitmap->LockBits(System::Drawing::Rectangle(0, 0, outputBitmap->Width, outputBitmap->Height), ImageLockMode::ReadWrite, PixelFormat::Format32bppArgb); | ||
+ | int bytes = Math::Abs(oBitmapData->Stride) * oBitmapData->Height; | ||
+ | array<Byte>^ outputBuffer = gcnew array<Byte>(bytes); | ||
+ | pin_ptr<Byte> oPin = &outputBuffer[0]; | ||
+ | Byte* pOPin = oPin; | ||
+ | |||
+ | // 交由 COM DLL 修改內容... | ||
+ | |||
+ | IntPtr oPtr = oBitmapData->Scan0; | ||
+ | System::Runtime::InteropServices::Marshal::Copy(outputBuffer, 0, oPtr, bytes); | ||
+ | outputBitmap->UnlockBits(oBitmapData); | ||
+ | </code> | ||
+ | |||
+ | C# 層的寫法則是先產生好一個 Bitmap,透過 ref Bitmap 的方式傳給 CLR 直接改記憶體內容,完成後即可用 PictureBox 顯示圖片 | ||
+ | |||
+ | <code> | ||
+ | Bitmap bmPic = new Bitmap(openFileDialog1.FileName); | ||
+ | clImgRetouch.SetWhiteBalance(ref bmPic); | ||
+ | pictureBox1.Image = (Image)bmPic; | ||
+ | </code> | ||
+ | |||
+ | ==== 參考文件 ==== | ||
+ | C# Pass by Reference to CLR | ||
+ | * [[http://msdn.microsoft.com/en-us/library/8903062a%28VS.90%29.aspx|% (Tracking Reference)]] | ||
+ | * [[http://msdn.microsoft.com/en-US/library/yk97tc08%28v=vs.90%29.aspx|^ (Handle to Object on Managed Heap)]] | ||
+ | * [[http://msdn.microsoft.com/en-US/library/te3ecsc8%28v=vs.90%29.aspx|gcnew]] | ||
+ | * [[http://msdn.microsoft.com/en-us/library/1dz8byfh%28v=vs.90%29.aspx|pin_ptr]] | ||
+ | * [[http://bobpowell.net/lockingbits.aspx|Using the LockBits method to access image data]] | ||
+ | |||
+ | WIC | ||
+ | * [[http://msdn.microsoft.com/en-us/library/windows/desktop/ff973956.aspx|Using the Windows Imaging Component]] | ||
+ | |||
+ | COM | ||
+ | * [[http://msdn.microsoft.com/zh-tw/library/aa288455%28v=vs.71%29.aspx|COM Interop 第 1 部份:C# 用戶端教學課程]] | ||
+ | * [[http://www.codeproject.com/Articles/38254/A-Beginner-Tutorial-for-Writing-Simple-COM-ATL-DLL|A Beginner Tutorial for Writing Simple COM/ATL DLL and Using it with .NET]] | ||