Trying to convert VB.NET to C++ Exportable DLL Function

I hate to resort to this, but I have spent the last few days poring through website after website, trying to figure out how to do the smallest things in C++. I have a headache I really would just like to see the correct solution:

The Problem:
I am trying to build a (simple) DLL to call from a VBA (Access 2007) application. I currently have 2 functions I wish to export, I'll post one below.

The problem I'm having with MS VC++ (aside from finding out how big of an idiot I am) is that I do not know ANY(apparantly) of the rules for C++. I finally got a small piece of code to actually compile after a few intense days of research, but working with anything other than the most simple data types is just too much for me right now. I am going to purchase a book, and try to learn from the ground up.

So here is a simple VB.NET (VS 2010) function. If someone could please convert it to C++ It would be very much appreciated. Hopefully I can learn enough from reviewing the conversion that I can get my feet on the C++ ground.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Imports System.Security.Cryptography
Module M1
    Public Function Encrypt(ByVal strText As String, ByVal strEncrKey As String) As String 'Returns encrypted string

        Dim IV() As Byte = {&H12, &H34, &H56, &H78, &H90, &HAB, &HCD, &HEF}
        Try
            Dim bykey() As Byte = System.Text.Encoding.UTF8.GetBytes(strEncrKey)
            Dim InputByteArray() As Byte = System.Text.Encoding.UTF8.GetBytes(strText)
            Dim des As New DESCryptoServiceProvider
            Dim ms As New IO.MemoryStream
            Dim cs As New CryptoStream(ms, des.CreateEncryptor(bykey, IV), CryptoStreamMode.Write)
            cs.Write(InputByteArray, 0, InputByteArray.Length)
            cs.FlushFinalBlock()
            Return Convert.ToBase64String(ms.ToArray())
        Catch ex As Exception
            Return ex.Message
        End Try
    End Function 'Encrypt

End Module 'M1 


I have gotten so many compile errors it's unreal. Once I figured out that C++ is case-sensitive, most of my errors disappearded. I have had HUGE problems getting this line converted:
Dim bykey() As Byte = System.Text.Encoding.UTF8.GetBytes(strEncrKey)
Depending on the different ways I have done it, I get errors like:
Error C3395: 'function' : __declspec(dllexport) cannot be applied to a function with the __clrcall calling convention
Error C2664: 'function' : cannot convert parameter number from 'type1' to 'type2' [using Encrypt( string strTExt)]

Anyway, I hope you'll help. If it's too much I understand, I'll just keep trying.

--Casey
C3395: You seem to have created a CLR C++ project. Create a Win32 C++ project.
C2664: This one isn't too clear.

You're going to have a hard time converting this. Let's see:
1. C++ doesn't have any text conversion (UTF-8), encoding (base64), or cryptography functions in its standard library. Either you provide them yourself, or you link in a library.
2. Interfacing with functions in a DLL that use C++ linkage is a huge pain if the program trying to link the DLL is oblivious to name mangling. VBA might be aware of it.

Since you already know .NET, can't you just write the DLL in .NET and link to it from VBA?
Last edited on
Hello helios,
I created a Win32 C++ Project, but enabled /clr for namespace usage
That gives me access to (supposedly) the same .NET namespaces I would get from VB

So far, I have the following which does compile, but I have not gotten far enought to test anything yet (although having it compile is a really big deal for me at this point):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <string>
#include <stdlib.h>

using namespace std;
using namespace System;
using namespace System::IO;
using namespace System::Text;
using namespace System::Security::Cryptography;

_declspec(dllexport) string Encrypt(string strText, string strEncrKey)
{
	// Initialize a Byte array with 8 bytes for Initial Vector of the Encryptor
	Byte IV[]= {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};
	
	//Convert std:string to System:String for the GetBytes() Function
	String^ s = gcnew String(strEncrKey.c_str());

	//Get the UTF8 Encoding for the System:String
	array<Byte>^ byKey = Encoding::UTF8->GetBytes(s);

	return "0";
}


I am slowly working my way through it...the C2664 Error was basically telling me that the GetBytes() function couldn't convert std::string (because it uses System::String)

1
2
3
4
5
6
7
8
9
10
11
12
13
>Main.cpp(20): error C2664: 'cli::array<Type> ^System::Text::Encoding::GetBytes(cli::array<wchar_t,dimension> ^)'
 : cannot convert parameter 1 from 'std::string' to 'cli::array<Type,dimension> ^'
1>          with
1>          [
1>              Type=unsigned char,
1>              dimension=1
1>          ]
1>          and
1>          [
1>              Type=wchar_t,
1>              dimension=1
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called


The 3395 Error apparantly is saying (in my own simple terms) that I cannot use a System::String parameter with a '^' for an exported function in a /clr enabled project althought I'm not deep enough to understand it all. I did work around it for now.

1
2
3
4
5
6
7
8
9
10
_declspec(dllexport) string Encrypt(String^ strText, String^ strEncrKey)
Raises the Error:
Main.cpp(12): error C3395: 'Encrypt' : __declspec(dllexport) cannot be applied to a function with the
 __clrcall calling convention

and

_declspec(dllexport) string Encrypt(String strText, String strEncrKey)
Raises the Error:
Main.cpp(11): error C3149: 'System::String' : cannot use this type here without a top-level '^'


So I settled on using a std::string parameter and converting it to System::String for the GetBytes() function.

To answer your last question...using the express version of VB.NET, you are very limited in creating a COM DLL. I could do it, but it becomes a huge hassel. It's a little better with the Ultimate version (it adds all the code you need, strong names the assembly, etc...), but ultimately (no pun intended) it still sucks. VB.NET is not built for COM. Therefore, I figured, "what the hell. I can program in 4 or 5 different languages, and I usually pick up on a new language within hours of just playing with it. I think I'll just hurry and get this done in C++ and be done with it!" LOL.

Well, now it's personal. I haven't figured out all the pointers and header files and deep crap yet, but I am determined to learn it now.
Last edited on
Ok. I got the code compiled with no errors / warnings. I guess now I'll set out to figure out what I have to do to complete the loop so I can attach to it via a Public Declare Function :(

So here's the code that worked...it may not be pretty, but it compiled LOL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "stdafx.h"
#include <string>
#include <stdlib.h>
#include <msclr\marshal_cppstd.h> 
 
using namespace std;
using namespace System;
using namespace System::IO;
using namespace System::Text;
using namespace System::Security::Cryptography;
using namespace msclr::interop;

_declspec(dllexport) string Encrypt(string strText, string strEncrKey)
{
	// Initialize a Byte array with 8 bytes for Initial Vector of the Encryptor
	array<Byte>^ IV= {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};
	
	//Handle exceptions
	try
	{
//Convert std:string to System:String for the GetBytes() Function
//Get the UTF8 Encoding for the System:String
	String^ s = gcnew String(strEncrKey.c_str());
	array<Byte>^ byKey = Encoding::UTF8->GetBytes(s);

//Convert std:string to System:String for the GetBytes() Function
//Get the UTF8 Encoding for the System:String
	String^ s1 = gcnew String(strText.c_str());
	array<Byte>^ InputByteArray = Encoding::UTF8->GetBytes(s1);

//Now create an instance of DESCryptoServiceProvider & MemoryStream and cross your fingers
	DESCryptoServiceProvider^ des = gcnew DESCryptoServiceProvider;
	MemoryStream^ ms = gcnew MemoryStream;

//Ok, here we go with the Cryptostream object! It took a little work, had to change the definition of IV
//from Byte IV[] = {x,x,x} to array<Byte>^ IV = {x,x,x}
	CryptoStream^ cs = gcnew CryptoStream(ms, des->CreateEncryptor(byKey, IV), CryptoStreamMode::Write);
	cs->Write(InputByteArray, 0, InputByteArray->Length);
	cs->FlushFinalBlock();

//Convert the result from System::String to std::string and return it
	String^ s2 = gcnew String(Convert::ToBase64String(ms->ToArray()));
	string s3 = marshal_as<std::string>(s2);
	return s3;
	}
	catch (exception& ex)
	{
	return ex.what();
	}
}


Aside from the junk at the end where I'm converting strings, does it look ok?
Topic archived. No new replies allowed.