Mac Office 2011 VBA and Dylib - string

I'm working on a Word 2011 plugin in Mac OS. Currently, I need to write a code in VBA Macro to retrieve a String from another application (through Socket communication). So, basically in Windows, I can simply make a DLL which help me to do Socket communication with the other application and return the String value to VBA Macro.
However, in Mac, I'm able to build a .dylib (in C) and using VBA to communicate with the dylib. However, I'm having a trouble with the return String. My simple C code is something like:
char * tcpconnect(char* arguments)
{}
First, it always contains Chr(0) characters. Secondly, I suspected that this C function will not be able to handle Unicode String.
Do you guys have any experiences or have any similar example of this?
Thanks,
David

My original post was an attempt to imitate SysAllocStringByteLen() using malloc(), but this will fail when Excel tries to free the returned memory. Using Excel to allocate the memory fixes that issue, and is less code as well, e.g.:
in 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;
}
Compile the above with
gcc -g -arch i386 -shared -o test.dylib test.c
Then, in a new VBA module, use the below and run "test", which will prepend "abc:" to the string "hi there" and output the result the debug window:
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

Related

Why does this code works well? It changes the Constant storage area string;

this is a simple linux kernel module code to reverse a string which should Oops after insmod,but it works well,why?
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static char *words = "words";
static int __init words_init(void)
{
printk(KERN_INFO "debug info\n");
int len = strlen(words);
int k;
for ( k = 0; k < len/2; k++ )
{
printk("the value of k %d\n",k);
char a = words[k];
words[k]= words[len-1-k];
words[len-1-k]=a;
}
printk(KERN_INFO "words is %s\n", words);
return 0;
}
static void __exit words_exit(void)
{
printk(KERN_INFO "words exit.\n");
}
module_init(words_init);
module_exit(words_exit);
module_param(words, charp, S_IRUGO);
MODULE_LICENSE("GPL");
static != const.
static in your example means "only visible in the current file". See What does "static" mean?
const just means "the code which can see this declaration isn't supposed to change the variable". But in certain cases, you can still create a non-const pointer to the same address and modify the contents.
Space removal from a String - In Place C style with Pointers suggests that writing to the string value shouldn't be possible.
Since static is the only difference between your code and that of the question, I looked further and found this: Global initialized variables declared as "const" go to text segment, while those declared "Static" go to data segment. Why?
Try static const char *word, that should do the trick.
Haha!,I get the answer from the linux kernel source code by myself;When you use the insmod,it will call the init_moudle,load_module,strndup_usr then memdup_usr function;the memdup_usr function will use kmalloc_track_caller to alloc memery from slab and then use the copy_from_usr to copy the module paragram into kernel;this mean the linux kernel module paragram store in heap,not in the constant storage area!! So we can change it's content!

Combining a static char* and a constant string

I want to be able to append a constant string to the end of another string in the form of a char*, and then use the resulting string as an argument for open(). Here's what it looks like:
file1.cpp
#include "string.h"
file2 foo;
char* word = "some";
foo.firstWord = word; //I want the file2 class to be able to see "some"
file2.h
#include <fstream>
#include <iostream>
#define SECONDWORD "file.txt"
class file2{
public:
file2();
static char* firstWord;
static char* fullWord;
private:
ofstream stream;
}
file2.cpp
#include "file2.h"
char* file2::firstWord;
char* file2::fullWord;
fullWord = firstWord + SECONDWORD; //so fullWord is now "somefile.txt" ,I know this doesn't work, but basically I am trying to figure out this part
file2::file2(){
stream.open(fullWord);
}
So I am not very well versed in C++, so any help would be appreciated!
C++-style solution might be the following.
#include <string>
char* a = "file";
char* b = ".txt";
...
stream.open((std::string(a) + b).c_str());
What happens here? First, std::string(a) creates a temporary std::string object. Than b value is added to it. At last, c_str() method returns a c-style string which contains a + b.

Calling unmanaged C DLL from VB CLR UDF in SQL Server

I am trying to integrate an existing C DLL (unmanaged obviously), that implements fuzzy matching, into SQL Server as a user defined function (UDF). The UDF is implemented with a CLR VB project. I have used this C code for nearly 20 years to do string matching on text files without a hitch. It has been compiled on about every platform under the sun and has never crashed or given erroneous results. Ever. Until now.
The usage of this UDF in a SQL SELECT statement looks something like this:
SELECT Field FROM Table WHERE xudf_fuzzy('doppler effect', Field) = 1;
xudf_fuzzy(Param1, Param2) = 1 is where the magic happens. Param1 is the clue word we are trying to match while Param2 is the field from the table to be tested against. If the match is successful within a certain number of errors, the UDF returns 1, if not it returns 0. So far so good.
Here is the CLR code that defines the fuzzy UDF and calls the C DLL:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports System.Text
Imports Microsoft.SqlServer.Server
Imports System.Runtime.InteropServices
Partial Public Class fuzzy
<DllImport("C:\Users\Administrator\Desktop\fuzzy64.dll", _
CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function setClue(ByRef clue As String, ByVal misses As Integer) As Integer
End Function
<DllImport("C:\Users\Administrator\Desktop\fuzzy64.dll", _
CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function srchString(ByRef text1 As String) As Integer
End Function
<Microsoft.SqlServer.Server.SqlFunction()> Public Shared Function _
xudf_fuzzy(ByVal strSearchClue As SqlString, ByVal strStringtoSearch As SqlString) As Long
Dim intMiss As Integer = 0
Dim intRet As Integer
Static Dim sClue As String = ""
xudf_fuzzy = 0
' we only need to set the clue whenever it changes '
If (sClue <> strSearchClue.ToString) Then
sClue = strSearchClue.ToString
intMiss = (Len(sClue) \ 4) + 1
intRet = setClue(sClue, intMiss)
End If
' return the fuzzy match result (0 or 1) '
xudf_fuzzy = srchString(strStringtoSearch.ToString)
End Function
Here is the front end of the C code being called. STRCT is where all of the global storage resides.
fuzzy.h
typedef struct {
short int INVRT, AND, LOWER, COMPL, Misses;
long int num_of_matched;
int D_length;
unsigned long int endposition, D_endpos;
unsigned long int Init1, NOERRM;
unsigned long int Mask[SYMMAX];
unsigned long int Init[MaxError];
unsigned long int Bit[WORDSIZE+1];
unsigned char prevpat[MaxDelimit];
unsigned char _buffer[Max_record+Max_record+256];
unsigned char _myPatt[MAXPAT];
} SRCH_STRUCT;
fuzzy.c
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <wtypes.h>
#include <time.h>
#include "fuzzy.h"
// call exports
__declspec(dllexport) int CALLBACK setClue(char**, int*);
__declspec(dllexport) int CALLBACK srchString(char**);
SRCH_STRUCT STRCT = { 0 };
int cluePrep(unsigned char []);
int srchMin(unsigned char [], int);
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
int CALLBACK setClue(char **pattern, int *misses)
{
int i;
unsigned char *p;
// code to do initialization stuff, set flags etc. etc.
STRCT.Misses = (int)misses;
p = &(STRCT._myPatt[2]);
STRCT._myPatt[0] = '\n';
STRCT._myPatt[1] = SEPCHAR;
strcpy((char *)p, *pattern);
//blah blah
// end setup stuff
i = cluePrep(STRCT._myPatt);
return 0;
}
int CALLBACK srchString(char **textstr)
{
int res,i = Max_record;
unsigned char c;
char *textPtr = *textstr;
STRCT.matched = 0;
//clean out any non alphanumeric characters while we load the field to be tested
while ((c = *textPtr++)) if ( isalpha(c) || isdigit(c) || c == ' ' ) STRCT._buffer[i++] = c;
STRCT._buffer[i] = 0;
// do the search
res = srchMin(STRCT.pattern, STRCT.Misses);
if (res < 0) return res;
return STRCT.matched;
}
The runtime library it is linked against is: Multi-threaded DLL (/MD)
The calling convention is: __cdecl (/Gd)
This is where it gets weird. If I have a regular dot-net application (code not shown) that grabs the entire recordset from the test database and iterates through all of the records one at a time calling this DLL to invoke the fuzzy match, I get back the correct results every time.
If I use the CLR UDF application shown above against the test database using the SQL statement shown above while using only one thread (a single core VM) I get back correct results every time as well.
When this DLL is used in CLR UDF mode on a multi-core machine then some of the results are wrong. The results are always a little off, but not consistently.
292 records should match and do in the first two test cases.
In the CLR UDF multi-threaded case the results will come back with 273, 284, 298, 290 etc.
All of the storage in the C DLL is in character arrays. No memory allocs are being used. It is also my understanding that if SQL Server is using this CLR app in multi-threaded mode that the threads are all assigned their own data space.
Do I need to somehow "pin" the strings before I send them to the C DLL? I just can't figure out how to proceed.
Pinning is not your issue, your C code is not thread-safe. The C code uses a global variable STRCT. There is only one instance of that global variable that will be shared across all threads. Each thread will update STRCT variable with different values, which would cause incorrect result.
You will have to refactor the C code so that it does not rely on the shared state of a global variable.
You may be able to get it to work by declaring STRCT with __declspec(thread) which will use Thread Local Storage so each thread gets its own copy of the variable. However, that would be unique to each unmanaged thread and there is no guarantee that there is a one-to-one mapping between managed and un-managed threads.
The better solution would be to get rid of the shared state completely. You could do this by allocating a SRCH_STRUCT in setClue and return that pointer. Then each call to srchString would take that SRCH_STRUCT pointer as a parameter. The VB.Net code would only have to treat this struct as in IntPtr, it would not need to know anything about the definition of SRCH_STRUCT. Note you would also need to add a new function to the DLL to deallocate the allocated SRCH_STRUCT.

Convert a String^ to wstring C++

I programmed a little Application in C++. There is a ListBox in the UI. And I want to use the selected Item of ListBox for an Algorithm where I can use only wstrings.
All in all I have two questions:
-how can I convert my
String^ curItem = listBox2->SelectedItem->ToString();
to a wstring test?
-What means the ^ in the code?
Thanks a lot!
It should be as simple as:
std::wstring result = msclr::interop::marshal_as<std::wstring>(curItem);
You'll also need header files to make that work:
#include <msclr\marshal.h>
#include <msclr\marshal_cppstd.h>
What this marshal_as specialization looks like inside, for the curious:
#include <vcclr.h>
pin_ptr<WCHAR> content = PtrToStringChars(curItem);
std::wstring result(content, curItem->Length);
This works because System::String is stored as wide characters internally. If you wanted a std::string, you'd have to perform Unicode conversion with e.g. WideCharToMultiByte. Convenient that marshal_as handles all the details for you.
I flagged this as a duplicate, but here's the answer on how to get from System.String^ to a std::string.
String^ test = L"I am a .Net string of type System::String";
IntPtr ptrToNativeString = Marshal::StringToHGlobalAnsi(test);
char* nativeString = static_cast<char*>(ptrToNativeString.ToPointer());
The trick is make sure you use Interop and marshalling, because you have to cross the boundary from managed code to non-managed code.
My version is:
Platform::String^ str = L"my text";
std::wstring wstring = str->Data();
With Visual Studio 2015, just do this:
String^ s = "Bonjour!";
C++/CLI
#include <vcclr.h>
pin_ptr<const wchar_t> ptr = PtrToStringChars(s);
C++/CX
const wchart_t* ptr = s->Data();
According to microsoft:
Ref How to: Convert System::String to Standard String
You can convert a String to std::string or std::wstring, without using PtrToStringChars in Vcclr.h.
// convert_system_string.cpp
// compile with: /clr
#include <string>
#include <iostream>
using namespace std;
using namespace System;
void MarshalString ( String ^ s, string& os ) {
using namespace Runtime::InteropServices;
const char* chars =
(const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
os = chars;
Marshal::FreeHGlobal(IntPtr((void*)chars));
}
void MarshalString ( String ^ s, wstring& os ) {
using namespace Runtime::InteropServices;
const wchar_t* chars =
(const wchar_t*)(Marshal::StringToHGlobalUni(s)).ToPointer();
os = chars;
Marshal::FreeHGlobal(IntPtr((void*)chars));
}
int main() {
string a = "test";
wstring b = L"test2";
String ^ c = gcnew String("abcd");
cout << a << endl;
MarshalString(c, a);
c = "efgh";
MarshalString(c, b);
cout << a << endl;
wcout << b << endl;
}
output:
test
abcd
efgh

cant convert parameter from char[#] to LPWSTR

When I compile this code in Visual C++, I got the below error. Can help me solve this issue..
DWORD nBufferLength = MAX_PATH;
char szCurrentDirectory[MAX_PATH + 1];
GetCurrentDirectory(nBufferLength, szCurrentDirectory);
szCurrentDirectory[MAX_PATH +1 ] = '\0';
Error message:
Error 5 error C2664: 'GetCurrentDirectoryW' : cannot convert parameter 2 from 'char [261]' to 'LPWSTR' c:\car.cpp
Your program is configured to be compiled as unicode. Thats why GetCurrentDirectory is GetCurrentDirectoryW, which expects a LPWSTR (wchar_t*).
GetCurrentDirectoryW expects a wchar_t instead of char array. You can do this using TCHAR, which - like GetCurrentDirectory - depends on the unicode setting and always represents the appropriate character type.
Don't forget to prepend your '\0' with an L in order to make the char literal unicode, too!
It seems you have define UNICODE, _UNICODE compiler flags. In that case, you need to change the type of szCurrentDirectory from char to TCHAR.
Headers:
#include <iostream>
#include <fstream>
#include <direct.h>
#include <string.h>
#include <windows.h> //not sure
Function to get current directory:
std::string getCurrentDirectoryOnWindows()
{
const unsigned long maxDir = 260;
wchar_t currentDir[maxDir];
GetCurrentDirectory(maxDir, currentDir);
std::wstring ws(currentDir);
std::string current_dir(ws.begin(), ws.end());
return std::string(current_dir);
}
To call function:
std::string path = getCurrentDirectoryOnWindows(); //Output like: C:\Users\NameUser\Documents\Programming\MFC Program 5
To make dir (Folder) in current directory:
std::string FolderName = "NewFolder";
std::string Dir1 = getCurrentDirectoryOnWindows() + "\\" + FolderName;
_mkdir(Dir1.c_str());
This works for me in MFC C++.

Resources