2013-02-20 54 views
1

我有一個使用OpenCV的C++/CLI項目。我自己在VS 2010中編譯了這個版本的OpenCV,我可以在沒有問題的情況下在非託管項目中使用它 - 當我試圖在託管項目中使用它時,麻煩就開始了。將STL字符串從C++/CLI應用程序傳遞到C++ DLL

感興趣的功能是cv::imread(std::string&, int)。只需從管理模塊調用它就根本不起作用,在接收端生成<無效指針>。我有點期待它。畢竟,託管代碼有自己的std::string實現。

當我創建一個單獨的C++文件,從模塊中刪除CLI支持並將代碼放入其中時,事情變得更有趣了。現在,imread正在獲取一個有效的指針,但其內容已被加密。顯然,string我傳遞它包含字符串指針偏移4個字節,但它預計它在0偏移量。

非託管模塊使用與OpenCV相同的CRT DLL,並將所有選項設置爲適合正常OpenCV使用的值。爲什麼它會有不同的string佈局?我搞不清楚了。

示例代碼:

#include <opencv/cv.h> 
#include <opencv/highgui.h> 

#include <string> 

using namespace cv; 
using namespace std; 

void Run() 
{ 
    string path("C:\\Users\\Don Reba\\Pictures\\Merlin 1D.jpg"); 

    Mat image(imread(path, CV_LOAD_IMAGE_GRAYSCALE)); 
    imwrite("image.jpg", image); 
} 
+0

嗯,你沒有顯示任何託管代碼,就像你通過你的文件路徑。也許你可以避開這個問題,通過傳遞一個'const char *'而不是一個字符串,並且在調用imread時將它留給調用的字符串ctor? – berak 2013-02-22 12:58:55

+0

代碼片段用於我的非託管模塊。當調用函數「Run」時,managed-> native轉換髮生。不幸的是,'imread'接受一個字符串引用,並且據我所知,在我身邊建立字符串是沒有辦法的。而且,這不是唯一的情況 - OpenCV始終使用字符串和向量。 – 2013-02-22 14:23:47

回答

0

問題是,Visual Studio 2010默認爲C++和C++/CLI項目使用不同的工具集。這就是爲什麼儘管設置相同,STL類仍然有不同的佈局。要解決此問題,請確保在C++/CLI項目中將配置屬性/常規/平臺工具集設置爲v100。

5

回答標題中的問題:不,你不能直接元帥的std ::從託管到非託管代碼串。請參閱another SO question的解答原因。主要原因是std :: string是一個模板,而不是「真正的」類型。

基本上,您需要編寫一個小的非託管模塊,它爲openCV函數提供簡單的包裝,從而擺脫STL類型。隨着你的榜樣作用,它可以如此簡單:

declspec(__dllexport) imread(char* c, int i) { 
    string s = c; 
    cv::imread(s, i); 
} 

至於用字符串偏移問題...嘗試創建一個單獨的項目,從一開始就「非託管」的類型。切換項目管理和背部能產生與項目設置一個爛攤子,有難以預料的後果 - 至少,我已經打了這樣凹坑兩次......

+0

我實際上可以編組一個'std :: string'從託管代碼到非託管。那裏沒問題。困難的部分是將它從一個非託管模塊傳遞給另一個模塊。因此,答案的相關部分僅僅是「將項目切換到管理層,後面可能會產生與項目設置混亂」,這不太令人滿意。 :) – 2013-02-28 13:37:24

+0

對不起,我真的不明白那個問題。 – DarkWanderer 2013-03-02 06:40:40

+0

至於「混亂的設置」,這是我的新鮮經驗 - 我遇到了同樣的問題(字符串開始處的4個未知符號 - 很可能是UTF-8 BOM)以及項目的簡單重新創建代碼幫助了我。 – DarkWanderer 2013-03-02 06:43:37

0

簡短的回答,你可以通過一個STL字符串從C++/CLI應用程序的本地C++ DLL,如果使用相同的編譯器設置爲DLL和C++/CLI應用程序。

代碼

#include <msclr/marshal_cppstd.h> // header for marshal utilities 

... 

String^ path = "C:\\Users\\Don Reba\\Pictures\\Merlin 1D.jpg"; // Managed string 
std::string s = msclr::interop::marshal_as<std::string>(path); // To stl string 
cv::imread(s, CV_LOAD_IMAGE_GRAYSCALE); 

詳情請參閱此頁:http://msdn.microsoft.com/en-us/library/bb384865.aspx

+0

我不明白爲什麼我想使用'marshal_as'而不是一個簡單的構造函數。問題在於儘管使用相同的編譯器設置,受管DLL中的std :: string具有不同的佈局。 – 2013-02-28 10:48:43

+0

當然你可以使用一個簡單的構造函數,但上面的代碼是演示如何編組一個託管的'String ^'到'std :: string'。至於佈局,如果編譯器設置相同,佈局也是一樣的。我非常確定這一點,因爲我已經以這種方式爲本地C++ dll創建了一個.NET包裝器DLL。如果您有編譯/鏈接錯誤,那麼在問題中發佈錯誤消息怎麼樣? – Chen 2013-02-28 14:32:04

0

你不應該(也不能)通過一個std ::不同的模塊(DLL)中,除非之間串&你確定所有的模塊都以相同的方式編譯(release和debug等)。

例如:如果你編譯發佈一個DLL和另一個作爲調試 - 那麼性病的內存佈局:: string的很可能是不同的。 其他編譯器設置也可能影響內存佈局。

試試這個 - 編譯下面的代碼版本與調試並運行它。 在調試中,您將在第28期中獲得32。

#include <iostream> 
#include <string> 

int main() 
{ 
    std::cout << "sizeof(std::string) : " << sizeof(std::string) << std::endl; 

    return 0; 
} 

我建議不要使用std :: string跨越模塊邊界。

相關問題