2012-03-29 58 views
1

我目前正在寫在MacOSX上一個C動態庫,該功能在Excel(2011)使用VBA使用,例如:返回字符串VBA在MacOSX上

Declare Function TestF Lib "path_to_lib:mylib.dylib" Alias "test" (ByRef res As String) As String 

這對於功能的工作原理罰款返回整數等,但我無法弄清楚如何將字符串傳遞迴VBA。我的C函數如下:

char* test(char *res) 
{ 
    res = "test"; 
    return res; 
} 

但調用VBA函數TestF作爲

Dim res As String 
res = TestF(res) 

崩潰Excel文檔... 如果我使用的是由MathLink的(數學)庫提供的功能,例如

此函數成功地將一個字符串分配給我可以在VBA中使用的「res」。關於MLGetString的信息可以在這裏找到:http://reference.wolfram.com/mathematica/ref/c/MLGetString.html

因此很明顯,這個功能能夠創建一個字符串,並將其傳遞給res,我可以使用VBA字符串即可。那麼,任何想法如何這個函數做到這一點,或者我怎麼可以傳遞一個字符串到VBA而不必使用OLE等?

回答

1

Strings in Visual Basic使用BSTR對象表示:它們由指針到一個空終止寬字符串(UTF-16)其由4個字節的長度的前綴之前。例如,下面是4個字符的字符串「測試」看起來像在內存中的小端機器上:

+-------------+-------+-------+-------+-------+-------+ 
| 08 00 00 00 | 74 00 | 65 00 | 73 00 | 74 00 | 00 00 | 
+-------------+-------+-------+-------+-------+-------+ 
Length:  't'  'e'  's'  't'  '\0' 
8 bytes 
(not including null) 

最重要的是,BSTR指針本身指向開始字符串(第一在這個例子中74字節),長度前綴。

對於閱讀BSTR S,你可以把它們當作普通的指針寬字符串,你會沒事的大部分。但是,如果你需要寫/創建它們,這有點棘手。

創建BSTR時,通常使用SysAllocString(及其親屬)分配它們,並使用SysFreeString釋放它們。您需要弄清楚OS X上的Excel如何公開這些函數(可能通過OLE動態庫),因爲它們不是標準C.如果您嘗試返回未通過SysAllocString分配的字符串,則Excel將會嘗試釋放它使用SysFreeString,這將可能會損壞您的堆和程序崩潰。所以不要這樣做。

還要注意,在默認情況下,一個wchar_t的大小爲4個字節,而不是2,在OS X上,這樣你就不能使用wchar_t數據類型與BSTR打交道時,S-你需要使用一個明確16-位的數據類型(如uint16_t),或與-fshort-wchar編譯選項來強制wchar_t爲2個字節進行編譯,但要知道的二進制不兼容問題,可能會導致。

+0

非常有幫助,謝謝!由於我在C(Mathematica的MathLink)中使用的其他庫顯然能夠將字符串傳遞給VBA而不使用OLE庫,我希望我也可以在我的函數中執行此操作?例如,當通過C函數將字符串參數傳遞給Excel時,函數[MLGetString](http://reference.wolfram.com/mathematica/ref/c/MLGetString.html)可以正常工作。 – Martin 2012-03-29 07:37:57

+0

這其實是錯誤的。在Mac OS X中,VBA中'BSTR'的內存佈局並不像你描述的那樣是一個窗口'BSTR'。它使用一個以空字符結尾的'char *'字符串的內存佈局。 – 2016-03-11 11:11:38

+0

在我的問題在這裏看到註釋2:http://stackoverflow.com/questions/35940136/bstrs-and-variants-under-mac-os-x – 2016-03-11 12:22:08

0

我原來的職位是使用malloc()模仿SysAllocStringByteLen()的嘗試,但是當Excel嘗試釋放返回的內存,這將失敗。使用Excel分配問題的內存修復程序,並且代碼也較少,例如:

在test.c的

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#define LPCSTR const char * 
#define LPSTR char * 
#define __declspec(dllexport) 
#define WINAPI 

char *saved_string = NULL; 
int32_t saved_len = -1; 

#define _CLEANUP if(saved_string) free(saved_string) 

__attribute__((destructor)) 
static void finalizer(void) { 
    _CLEANUP; 
} 

int32_t __declspec(dllexport) WINAPI get_saved_string(LPSTR pszString, int cSize) { 
    int32_t old_saved_len = saved_len; 
    if(saved_len > 0 && cSize >= saved_len) 
    strncpy(pszString, saved_string, saved_len); 
    if(saved_string) { 
    free(saved_string); 
    saved_string = NULL; 
    saved_len = -1; 
    } 
    return old_saved_len; 
} 

int32_t __declspec(dllexport) WINAPI myfunc(LPCSTR *pszString) { 
    int len = (pszString && *pszString ? strlen(*pszString) : 0); 
    saved_string = malloc(len + 5); 
    saved_len = len + 5; 
    sprintf(saved_string, "%s%.*s", "abc:", len, *pszString); 
    return saved_len; 
} 

編譯上面

gcc -g -arch i386 -shared -o test.dylib test.c 

然後,新VBA模塊中,使用下面和運行 「測試」,這將在前面加上「ABC :「到字符串」hi there「並輸出調試窗口的結果:

Public Declare Function myfunc Lib "<colon-separated-path>:test.dylib" (s As String) As Long 
Public Declare Function get_saved_string Lib "<colon-separated-path>:test.dylib" (ByVal s As String, ByVal csize As Long) As Long 

Option Explicit 

Public Function getDLLString(string_size As Long) As String 
    Dim s As String 
    If string_size > 0 Then 
     s = Space$(string_size + 1) 
     get_saved_string s, string_size + 1 
    End If 
    getDLLString = s 
End Function 

Public Sub test() 
Debug.Print getDLLString(myfunc("hi there")) 
End Sub 
+0

我添加了上述(無C++)的C版本在http: //stackoverflow.com/questions/9833808/mac-office-2011-vba-and-dylib/25023122#25023122。基本上,我只是刪除了C++字符串的使用,這有利於在使用gcc編譯時不改變名稱。我保留上面的答案是因爲有些人可能更喜歡C++,並且從上面修改爲純C非常容易。 – mwag 2014-09-02 17:31:48

0

其實,BSTR在VBA下的內存佈局在mac os x (辦公室2011 excel讓我們說)不是亞當羅森菲爾德的答案中描述的Windows BSTR之一。它使用替代內存佈局的空字符char *字符串。

在我的問題在這裏例如見備註2:

BSTR's and VARIANT's under... mac os x

現在,你的問題是:你對C++/dylib側char *分配內存,windows下這種記憶將是釋放在VBA端,而Mac OS X這是不可能的,因爲釋放任務不能傳遞給另一個進程,因爲它可以在Windows下...