Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/actionscript-3/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将字符串从VBA传递到C++;动态链接库 我真的很困惑把VBA字符串传递给C++。以下是VBA代码: Private声明子passBSTRVal Lib“foo.dll”(ByVal s作为字符串) 私有声明子passbstref Lib“foo.dll”(ByRef s作为字符串) 私有声明子passByNearoval Lib“foo.dll”(ByVal s作为字符串) 私有声明子passByNearRef库“foo.dll”(ByRef s作为字符串) 私有声明子passByWideVal Lib“foo.dll”(ByVal s作为字符串) 私有声明子passByWideRef库“foo.dll”(ByRef s作为字符串) 副词foobar() Dim s作为字符串,str作为字符串 str=“你好,世界!” s=str 呼叫passByBSTRVal(s) s=str 呼叫PassbyBSTREF(s) s=str 呼叫PassbyNerrowVal(s) s=str 呼叫PassbyNearRef(s) s=str 呼叫passByWideVal(s) s=str 呼叫passByWideRef(s) 端接头 和C++ DLL代码: void __stdcall passByBSTRVal( BSTR s ) { MessageBox(NULL, s, L"Pass BSTR by value", MB_OK | MB_ICONINFORMATION); } void __stdcall passByBSTRRef( BSTR *s ) { MessageBox(NULL, *s, L"Pass BSTR by ref", MB_OK | MB_ICONINFORMATION); } void __stdcall passByNarrowVal( LPCSTR s ) { USES_CONVERSION; MessageBox(NULL, A2W(s), L"Pass by Narrow Val", MB_OK | MB_ICONINFORMATION); } void __stdcall passByNarrowRef( LPCSTR* s ) { USES_CONVERSION; MessageBox(NULL, A2W(*s), L"Pass by Narrow Ref", MB_OK | MB_ICONINFORMATION); } void __stdcall passByWideVal( LPCWSTR s ) { MessageBox(NULL, s, L"Pass by Wide Val", MB_OK | MB_ICONINFORMATION); } void __stdcall passByWideRef( LPCWSTR* s ) { MessageBox(NULL, *s, L"Pass by Wide Ref", MB_OK | MB_ICONINFORMATION); }_C++_Excel_Vba - Fatal编程技术网

将字符串从VBA传递到C++;动态链接库 我真的很困惑把VBA字符串传递给C++。以下是VBA代码: Private声明子passBSTRVal Lib“foo.dll”(ByVal s作为字符串) 私有声明子passbstref Lib“foo.dll”(ByRef s作为字符串) 私有声明子passByNearoval Lib“foo.dll”(ByVal s作为字符串) 私有声明子passByNearRef库“foo.dll”(ByRef s作为字符串) 私有声明子passByWideVal Lib“foo.dll”(ByVal s作为字符串) 私有声明子passByWideRef库“foo.dll”(ByRef s作为字符串) 副词foobar() Dim s作为字符串,str作为字符串 str=“你好,世界!” s=str 呼叫passByBSTRVal(s) s=str 呼叫PassbyBSTREF(s) s=str 呼叫PassbyNerrowVal(s) s=str 呼叫PassbyNearRef(s) s=str 呼叫passByWideVal(s) s=str 呼叫passByWideRef(s) 端接头 和C++ DLL代码: void __stdcall passByBSTRVal( BSTR s ) { MessageBox(NULL, s, L"Pass BSTR by value", MB_OK | MB_ICONINFORMATION); } void __stdcall passByBSTRRef( BSTR *s ) { MessageBox(NULL, *s, L"Pass BSTR by ref", MB_OK | MB_ICONINFORMATION); } void __stdcall passByNarrowVal( LPCSTR s ) { USES_CONVERSION; MessageBox(NULL, A2W(s), L"Pass by Narrow Val", MB_OK | MB_ICONINFORMATION); } void __stdcall passByNarrowRef( LPCSTR* s ) { USES_CONVERSION; MessageBox(NULL, A2W(*s), L"Pass by Narrow Ref", MB_OK | MB_ICONINFORMATION); } void __stdcall passByWideVal( LPCWSTR s ) { MessageBox(NULL, s, L"Pass by Wide Val", MB_OK | MB_ICONINFORMATION); } void __stdcall passByWideRef( LPCWSTR* s ) { MessageBox(NULL, *s, L"Pass by Wide Ref", MB_OK | MB_ICONINFORMATION); }

将字符串从VBA传递到C++;动态链接库 我真的很困惑把VBA字符串传递给C++。以下是VBA代码: Private声明子passBSTRVal Lib“foo.dll”(ByVal s作为字符串) 私有声明子passbstref Lib“foo.dll”(ByRef s作为字符串) 私有声明子passByNearoval Lib“foo.dll”(ByVal s作为字符串) 私有声明子passByNearRef库“foo.dll”(ByRef s作为字符串) 私有声明子passByWideVal Lib“foo.dll”(ByVal s作为字符串) 私有声明子passByWideRef库“foo.dll”(ByRef s作为字符串) 副词foobar() Dim s作为字符串,str作为字符串 str=“你好,世界!” s=str 呼叫passByBSTRVal(s) s=str 呼叫PassbyBSTREF(s) s=str 呼叫PassbyNerrowVal(s) s=str 呼叫PassbyNearRef(s) s=str 呼叫passByWideVal(s) s=str 呼叫passByWideRef(s) 端接头 和C++ DLL代码: void __stdcall passByBSTRVal( BSTR s ) { MessageBox(NULL, s, L"Pass BSTR by value", MB_OK | MB_ICONINFORMATION); } void __stdcall passByBSTRRef( BSTR *s ) { MessageBox(NULL, *s, L"Pass BSTR by ref", MB_OK | MB_ICONINFORMATION); } void __stdcall passByNarrowVal( LPCSTR s ) { USES_CONVERSION; MessageBox(NULL, A2W(s), L"Pass by Narrow Val", MB_OK | MB_ICONINFORMATION); } void __stdcall passByNarrowRef( LPCSTR* s ) { USES_CONVERSION; MessageBox(NULL, A2W(*s), L"Pass by Narrow Ref", MB_OK | MB_ICONINFORMATION); } void __stdcall passByWideVal( LPCWSTR s ) { MessageBox(NULL, s, L"Pass by Wide Val", MB_OK | MB_ICONINFORMATION); } void __stdcall passByWideRef( LPCWSTR* s ) { MessageBox(NULL, *s, L"Pass by Wide Ref", MB_OK | MB_ICONINFORMATION); },c++,excel,vba,C++,Excel,Vba,我的期望是,对passByBSTRVal和passbybstref的前两个调用都能正常工作。为什么?因为VBA字符串是COM BSTR对象。然而,在通过C++代码的同时,这两个函数的S值是垃圾(一堆汉字)。此外,显示的消息框是(相同的)。我真的很惊讶前两个功能不起作用 我的下一个期望是对PassbyNearVal和PassbyNearRef的第二次调用不起作用,因为BSTR被定义为“typedef ollechar*BSTR”,而ollechar是宽字符类型,而LPCSTR是窄字符类型。然而,与

我的期望是,对passByBSTRVal和passbybstref的前两个调用都能正常工作。为什么?因为VBA字符串是COM BSTR对象。然而,在通过C++代码的同时,这两个函数的S值是垃圾(一堆汉字)。此外,显示的消息框是(相同的)。我真的很惊讶前两个功能不起作用

我的下一个期望是对PassbyNearVal和PassbyNearRef的第二次调用不起作用,因为BSTR被定义为“typedef ollechar*BSTR”,而ollechar是宽字符类型,而LPCSTR是窄字符类型。然而,与我的预期相反,这两个功能实际上起了作用。当我通过C++代码时,参数S正好是我所期望的。我的期望又错了

最后,我对最后两个函数(pass by wide val和ref)的期望是它们可以工作,因为OLECHAR是一个宽字符字符串,所以LPCWSTR应该能够指向BSTR。但正如案例1(我想这两个案例是相同的)一样,我的预期是错误的。参数s由垃圾字符组成(MessageBox显示相同的垃圾字符)


为什么我的直觉完全错了?有人能解释一下我不理解的地方吗?

这种形式的外部函数调用与早期版本的Visual Basic兼容,并继承了它们的语义。特别是,VB3在16位窗口上运行,只处理ANSI(即MBCS)字符串

Declare
语法具有相同的限制。VBA转换字符串的前提是它正在将字符串从UTF-16转换为ASCII。这允许用VB3编写的代码在VB4、VB5和VB6中不受影响地工作

例如,“AZ”以
\u0041\u005A
开头,转换为ANSI,变成
\x41\x5A
,重新解释为
\u5A41
,即婁".

(使用VB4,Microsoft将WordBasic、Excel Basic和Visual Basic合并为一种语言VBA。)

从VBA调用函数的“新”方法是使用MIDL为需要使用的外部函数创建一个类型库,并将其添加为项目的引用。类型库可以描述函数的确切签名,(例如,
BSTR
LPCSTR
LPCWSTR
[out]BSTR*
,等等)特别是,从VBA调用函数时,不需要将函数包装在COM对象中(尽管如果希望从VBScript调用函数,则需要这样做)

  • 一组DLL函数被描述为MIDL
    模块

或者,你不必费心为一个函数启动
midl
,你可以使用
VarPtr
/
StrPtr
/
CopyMemory
hack。这相当于
PEEK
POKE
大注:我不是程序员,我只是很喜欢编程,所以请善待我我。我想提高,所以非常欢迎比我更熟练的人(基本上是每个人)的建议和评论

本,如果你正在读这篇文章,我想你让我看到了正在发生的事情。MIDL听起来是正确的做法,我打算学习它,但这似乎是一个很好的学习机会,我从来没有让这些让我错过

我认为现在的情况是,窄字符被编组到宽字符存储中。例如,使用窄字符存储的字符串“hello”如下所示:

|h |e |l |l |o |\0 |
|h   |e   |l   |l   |o   |\0   |
并以宽字符存储,如下所示:

|h |e |l |l |o |\0 |
|h   |e   |l   |l   |o   |\0   |
<>但是,当你把字符串从VBA传递到C++时,会发生一些奇怪的事情。你把窄字符编成一个宽字符,比如:

|h e |l l |o \0 |    |    |    |
这就是为什么使用LPCSTR/LPCSTR*有效。是的,BSTR使用了一个wchar_t字符串,但这种编组使它看起来像一个char字符串。使用char*访问时交替指向wchar_t每一半中的第一个和第二个字符(h,然后是e.l,然后是l.o,然后是\0)。尽管char*和wchar_t*的指针算法不同,但它之所以有效,是因为字符的编组方式很有趣。事实上,我们传递了一个指向数据字符串的指针,但如果您想访问BSTR的长度,即数据字符串前4个字节,您可以使用指针算法玩游戏,以达到您想要的目的。假设BSTR作为LPCSTR s传入

char* ptrToChar;      // 1 byte
wchar_t* ptrToWChar;  // 2 bytes
int* ptrToInt;        // 4 bytes
size_t strlen;

ptrToChar = (char *) s;
strlen = ptrToChar[-4];

ptrToWChar = (wchar_t *) s;
strlen = ptrToWChar[-2];

ptrToInt = (int *) s;
strlen = ptrToInt[-1];
当然,如果字符串作为LPCSTR*s传入,那么您当然需要首先通过以下方式访问来解除对s的引用:

ptrToChar = (char *)(*s);
等等

如果想使用LPCWSTR或BSTR来接收VBA字符串,则必须在这个编组中跳舞。例如,为了创建一个将VBA字符串转换成大写的C++ DLL,我做了以下的操作:

BSTR __stdcall pUpper( LPCWSTR* s )
{
    // Get String Length (see previous discussion)
    int strlen = (*s)[-2];

    // Allocate space for the new string (+1 for the NUL character).
    char *dest = new char[strlen + 1];

    // Accessing the *LPCWSTR s using a (char *) changes what we mean by ptr arithmetic,
    // e.g. p[1] hops forward 1 byte.  s[1] hops forward 2 bytes.
    char *p = (char *)(*s);

    // Copy the string data
    for( int i = 0; i < strlen; ++i )
        dest[i] = toupper(p[i]);

    // And we're done!
    dest[strlen] = '\0';

    // Create a new BSTR using our mallocated string.
    BSTR bstr = SysAllocStringByteLen(dest, strlen);

    // dest needs to be garbage collected by us.  COM will take care of bstr.
    delete dest;
    return bstr;
}
BSTR\uu stdcall pUpper(LPCWSTR*s)
{
//获取字符串长度(请参阅前面的讨论)
int strlen=(*s)[-2];
//为新字符串分配空间(NUL字符为+1)。
char*dest=新字符[strlen+1];
//使用访问*LPCWSTR
; MidlForModules.def : Declares the module parameters.

LIBRARY

EXPORTS
    DllCanUnloadNow     PRIVATE
    DllGetClassObject   PRIVATE
    DllRegisterServer   PRIVATE
    DllUnregisterServer PRIVATE
    DllInstall      PRIVATE
    _MyAbs @656
    pUpper
Option Explicit

Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long

Private Sub Workbook_Open()
    '* next line establishes relative position of Dll
    Debug.Assert Dir(ThisWorkbook.Path & "\IDLForModules.dll") = "IDLForModules.dll"

    '* next line loads the Dll so we can avoid very long Lib "c:\foo\bar\baz\barry.dll"
    LoadLibrary ThisWorkbook.Path & "\IDLForModules.dll"

    '* next go to  Tools References are check "Idl For Modules"
    '* "Idl For Modules" Iis set in the IDL with helpstring("Idl For Modules")

End Sub
Option Explicit

Sub TestAbs()
    Debug.Print IDLForModulesLib.Math.Abs(-5)
End Sub

Sub TestUpper()
    Debug.Print IDLForModulesLib.Strings.Upper("foobar")
End Sub
  STDAPI ToUpperLPWSTR(LPCWSTR in, LPWSTR out, int cch)
  {
    // unicode version
    LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LINGUISTIC_CASING | LCMAP_UPPERCASE, in, lstrlenW(in), out, cch);
    return S_OK;
  }

  STDAPI ToUpperBSTR(BSTR in, BSTR out, int cch)
  {
    // unicode version
    // note the usage SysStringLen here. I can do it because it's a BSTR
    // and it's slightly faster than calling lstrlen...
    LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LINGUISTIC_CASING | LCMAP_UPPERCASE, in, SysStringLen(in), out, cch);
    return S_OK;
  }

  STDAPI ToUpperLPSTR(LPCSTR in, LPSTR out, int cch)
  {
    // ansi version
    LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LINGUISTIC_CASING | LCMAP_UPPERCASE, in, lstrlenA(in), out, cch);
    return S_OK;
  }
  Private Declare PtrSafe Function ToUpperLPWSTR Lib "foo.dll" (ByVal ins As LongPtr, ByVal out As LongPtr, ByVal cch As Long) As Long
  Private Declare PtrSafe Function ToUpperBSTR Lib "foo.dll" (ByVal ins As LongPtr, ByVal out As LongPtr, ByVal cch As Long) As Long
  Private Declare PtrSafe Function ToUpperLPSTR Lib "foo.dll" (ByVal ins As String, ByVal out As String, ByVal cch As Long) As Long

  Sub Button1_Click()

      Dim result As String
      result = String(256, 0)

      // note I use a special character 'é' to make sure it works
      // I can't use any unicode character because VBA's IDE has not been updated and does not suppport the
      // whole unicode range (internally it does, but you'll have to store the texts elsewhere, and load it as an opaque thing w/o the IDE involved)

      ToUpperLPWSTR StrPtr("héllo world"), StrPtr(result), 256
      MsgBox result
      ToUpperBSTR StrPtr("héllo world"), StrPtr(result), 256
      MsgBox result
      ToUpperLPSTR "héllo world", result, 256
      MsgBox result
  End Sub
Declare PtrSafe Function GetWindowsDirectoryW Lib "kernel32" _ 
   (ByVal lpBuffer As LongPtr, ByVal nSize As Long) As Long

Sub TestGetWindowsDirectoryW()
  Dim WindowsDir As String
  WindowsDir = Space$(256)
  GetWindowsDirectoryW StrPtr(WindowsDir), 256
  MsgBox WindowsDir
End Sub