2010-09-20 66 views
2

我有一個實現IDispatch接口的類(JSObject)。該類暴露於在託管的Web瀏覽器控件(IWebBrowser2)中運行的JavaScript。從C++函數返回字符串爲JavaScript

這裏瞭解如何工作參見:Calling C++ function from JavaScript script running in a web browser control

我可以從我的JavaScript代碼調用到JSObject,並且可以接收返回整數/多頭。但是當函數返回一個字符串(BSTR)時會出錯。

這是IDispatch::Invoke()代碼的一部分:

int lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "Returned string", -1, 
    NULL, 0); 
BSTR bstrRet = SysAllocStringLen(0, lenW); 
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "Returned string", -1, bstrRet, 
    lenW); 

pVarResult->vt = VT_BSTR; 
pVarResult->bstrVal = bstrRet; 

// Who calls SysFreeString(bstrRet);? 

與上面的代碼,你可以alert()返回的字符串,但不能添加到它。 alert(returnedString + "foo");只會顯示「返回的字符串」。 「foo」部分未添加到字符串中。不知怎的,字符串的結尾似乎有些問題。任何想法的人?我是不是在打SysFreeString()

編輯:

我暫時列入atlbase.h所以我可以使用CComBSTR。上面的代碼現在看起來是這樣的:

pVarResult->vt = VT_BSTR; 
pVarResult->bstrVal = CComBSTR("test string"); 

通過代碼步進明確表明pVarResult是「測試字符串」一路,直到函數返回。但是,當我在JavaScript代碼中alert()返回的字符串時,我得到「擴展」。 alert(returnedString + "foo")是「expandedfoo」。所以這是向正確方向邁出的一小步,因爲您可以添加到返回的字符串中。但它也是在錯誤的方向作爲返回串的步驟是不是我真的回來......

*pVarResult = CComVariant("test string"); 

該代碼給出了相同的結果在前面的列表中(使用的CComBSTR)的代碼。

+1

我沒有看到它(用於ATL),但僅供參考:Out參數由調用者擁有,因此調用者可以釋放該字符串。請參閱[內存管理規則](http://msdn.microsoft.com/en-us/library/ms810016.aspx)。 – 2010-09-20 21:22:50

+0

@Georg:太好了。所以至少我沒有泄漏記憶。 – Tobbe 2010-09-20 21:59:20

回答

1

第一個MultiByteToWideChar()調用返回存儲字符串所需的字符數量,包括空終止符。然後SysAllocStringLen()lenW+1個字符分配一個緩衝區(比需要的多一個)並且已經以null結尾。

由於MultiByteToWideChar()還寫入了一個空終止符,所以最後在字符串的末尾會出現兩個。對於BSTR,嵌入的空字符是可能的,因爲它們的長度是前綴,所以JScript實現可能會連接而不刪除附加的字符......因此,最終會得到一個字符串,其中間只有一個嵌入的空字符部分。

長話短說,固定長度:

lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, NULL, 0); 
bstrRet = SysAllocStringLen(0, lenW-1); 
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, bstrRet, lenW-1); 

正如在註釋中,字符串是被調用者釋放 - 在memory management rules決定了輸出參數被調用者所有。

+0

所以,只是爲了確保我得到這個。在最後的MultiByteToWideChar調用中,我們可以只使用lenW和lenW-1,對吧?對於lenW-1,我們不會複製str中的\ 0,並使用已經爲我們提供的一個SysAllocStringLen。用lenW我們覆蓋了用str創建的\ 0 SysAllocStringLen。正確? – Tobbe 2010-09-21 11:43:31

+0

@Tobbe:正確,用上面的代碼'SysAllocStringLen()'爲'lenW'字符分配一個緩衝區,並且你會覆蓋最後一個空終止符。 – 2010-09-21 22:28:27

0

在一些有趣的事情下面調試代碼變得明顯。

int lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "testing testing", -1, 
    NULL, 0); 
BSTR bstrRet = SysAllocStringLen(0, lenW); 
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "testing testing", -1, bstrRet, 
    lenW); 

BSTR bstrRet2 = SysAllocString(L"testing testing"); 

int len1 = SysStringByteLen(bstrRet); 
int len2 = SysStringByteLen(bstrRet2); 

len1爲32 len2只是30 SysAllocString作品與JavaScript代碼我有,另一種方法則沒有。

看看分配bstrRet的內存,我可以看到它以0x00 0x00 0x00 0x00結尾,而bstrRet2只有0x00 0x00。所以我的猜測是,當使用bstrRet將其拋出時,會向JavaScript代碼發送額外的空終止符。這就是爲什麼你不能附加任何東西。 bstrRet2沒有額外的空終止符。

知道,在這個問題的原代碼,可向工作,如果它的修改是這樣的:

int lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "Returned string", -1, 
    NULL, 0) - 1; 
BSTR bstrRet = SysAllocStringLen(0, lenW); 
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "Returned string", lenW*2, bstrRet, 
    lenW); 

pVarResult->vt = VT_BSTR; 
pVarResult->bstrVal = bstrRet; 

我不知道如果這樣做lenW*2是安全的,但該代碼似乎工作這麼遠在我所做的有限測試。