Solved

translating a windows app from VB.NEt to C++/C# , or how to use the same GUI in different platforms

Posted on 2008-06-17
18
402 Views
Last Modified: 2013-11-26
We are developing a .NET DLL to control our hardware.
For this we want to provide a couple of demo applications in different programming languages.
We currently have a demo in VB.NET.

We would like to have exaclty the same application also in C++ / C#.

Is there a way to achieve this without starting all over again from scratch? can we somehow reuse the controls and their properties (size, location, settings) in another language.

0
Comment
Question by:m8online
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 7
18 Comments
 
LVL 10

Expert Comment

by:athapa
ID: 21801841
To copy your Forms,
open both projects in separate IDEs,
open the Form you'd like to copy,
Select All from the source Form and Paste into your new form in C# (or even C++/CLI).

For your code, you could use the tools like RemoteSoft's Salamander which will convert you existing assembly (compiled dll or exe) into C# or C++/CLI source code. If all you're trying to do is Vb.Net to C# then you can use a whole bunch of free online translation of Vb.Net to C#.  Converting to C++ is slightly tricky and that's where you'd want to use tools like Salamander.
0
 
LVL 1

Author Comment

by:m8online
ID: 21853681
I haven't got around to testing the GUI copying thing yet. This will probably work. Sometimes things are so simple that you just cannot see them. thanks for that.

About Salamander. I took a look at their website. It looks like a rather expensive tool.
Did you work with it yourself? What exactly does it do? will it also maintain the variable names used?

Would there also be an open source tool that does a basic translation?  It would be ok for me to have to clean up the generated code later.
0
 
LVL 10

Expert Comment

by:athapa
ID: 21854924
I've use Salamander myself. If you have corresponding pdb files (generated while compiling in debug mode) for your exe or dll then all of your internal variable names will also get regenerated. Even without a .pdb files the regenerated codes are still readable. Besides Salamander there are few others commercial ones but I've not used them. There was one open source which I used long time back, Anakrino, but it hasn't been actively developed in a while.

As I mentioned before if all you need to do is Vb.net to C# then try these first before buying Salamander or other tools.
http://labs.developerfusion.co.uk/convert/vb-to-csharp.aspx
http://www.carlosag.net/Tools/CodeTranslator/
http://www.dotnetspider.com/convert/Vb-To-Csharp.aspx
http://www.kamalpatel.net/ConvertCSharp2VB.aspx

There are some windows apps too if you prefer not to use the online ones.
0
How Do You Stack Up Against Your Peers?

With today’s modern enterprise so dependent on digital infrastructures, the impact of major incidents has increased dramatically. Grab the report now to gain insight into how your organization ranks against your peers and learn best-in-class strategies to resolve incidents.

 
LVL 1

Author Comment

by:m8online
ID: 21893585
I tried copy-pasting from the VB.net project to a C++ project. This did not work.
The paste action just did not work.
Would one of the suggested tools also transfer the GUI?

I am thinking that I might have the wrong approach. A C# user would probable understand the VB.NET example application. Especially because it is nothing more than some glue between a GUI and the DLL which they would actually need.

The difference between VB.NET and C++ is more of a problem because the DLL is in fact a class library. For using it in C++ (no, user would not want to compiler with /clr) it would need to be converted to a classical DLL interface.
For this I am working on a wrapper DLL in C++ which would make an instance of the class and couple its member functions to the classical DLL interface.
This classical DLL could also be used from VB.net. So I could adapt the example app. to work with the two different DLL interfaces.

Current problem is that I cannot get the wrapper DLL to compile correctly.
In the code below the class CL_IO_ctrl is not recognized.

Any ideas?
// M8_IO_ctrl_classic.cpp : Defines the initialization routines for the DLL.
//
#using "C:\temp\IO_Ctrl\USB_IO_211_demo_app\V1_0_0\bin\M8_IO_ctrl.dll"
#include "stdafx.h"
#include "M8_IO_ctrl_classic.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
#define API_EXPORT __declspec(dllexport)
 
//
//	Note!
//
//		If this DLL is dynamically linked against the MFC
//		DLLs, any functions exported from this DLL which
//		call into MFC must have the AFX_MANAGE_STATE macro
//		added at the very beginning of the function.
//
//		For example:
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// normal function body here
//		}
//
//		It is very important that this macro appear in each
//		function, prior to any calls into MFC.  This means that
//		it must appear as the first statement within the 
//		function, even before any object variable declarations
//		as their constructors may generate calls into the MFC
//		DLL.
//
//		Please see MFC Technical Notes 33 and 58 for additional
//		details.
//
 
// CM8_IO_ctrl_classicApp
 
BEGIN_MESSAGE_MAP(CM8_IO_ctrl_classicApp, CWinApp)
END_MESSAGE_MAP()
 
 	 class Cl_IO_ctrl IO_ctrl;
 
 
 
// CM8_IO_ctrl_classicApp construction
 
CM8_IO_ctrl_classicApp::CM8_IO_ctrl_classicApp()
{
// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}
 
 
// The one and only CM8_IO_ctrl_classicApp object
 
CM8_IO_ctrl_classicApp theApp;
 
 
// CM8_IO_ctrl_classicApp initialization
 
BOOL CM8_IO_ctrl_classicApp::InitInstance()
{
	CWinApp::InitInstance();
 
	return TRUE;
}

Open in new window

0
 
LVL 10

Expert Comment

by:athapa
ID: 21895474
I tried copying vb.net form from the designer and paste in c++.
Here is what I did.
Open the vb.net solution.
Add a new c++ clr project
Open the form that needs to be copied in design mode.
Ctrl+A to copy all elements of the form
Add a new form in c++ file in cpp project
Go to design view of the new form
Ctrl+V to paste

But the method above or any of the tools mentioned previously will only work if you are trying to create c++/clr executables (or dll). If you are trying to use mfc or other framework for your ui then this method won't work.

If I understand you correctly then you're trying to create a wrapper for some 3rd party c++ component which (the wrapper) you'd like to use in dotnet applications. Is this correct? If so, do you need to use those component in c++ applications too?

0
 
LVL 1

Author Comment

by:m8online
ID: 21896713
No, it is the other way around.

I have created a class library DLL in VB.NET, and a VB.NET example app to show how to use this class library.

Now I want this class library also to be accessible to C++ apps.
I know that if C++ is compiled managed for .NET (/clr) it should be able to use a class library directly. However, as my customer wants his C++ environment to remain unmanaged, this is not an option for him.

Therefore I am trying to create a wrapper for the  class library. I Guessed the wrapper should be written in managed C++ and then expose al the member functions in the classical DLL way.

Then, because I am trying to avoid having more than one example app. I would use the same VB.NET example app, only now using the classical (GetProcaddress like) way to access the functions.
0
 
LVL 10

Expert Comment

by:athapa
ID: 21913004
If you can, read this book
Expert C++/CLI:
.NET for Visual C++ Programmers

This book covers a lot of topic about manged code accessing unmanaged codes and vice versa.

I'm not sure if you'll be able to load vb.net classes using loadlibrary and getprocaddress. It probably would be easier for you to create a wrapper in c++ instead.

Your other alternative would be to use comvisible attribute in your vb.net class and access the class as com from c++.

Here is another approach but it seems convoluted.
0
 
LVL 1

Author Comment

by:m8online
ID: 21913519
Yes, Writing a wrapper DLL was indeed the way I wanted to go.

The problem which I am having with the wrapper DLL is that at the moment I cannot get it to recognize the VB.net managed class library.

I added the /CLR compiler switch
I added the VB:net managed DLL to "references"

Still the compiler says: undefined class: Cl_IO_ctrl
0
 
LVL 10

Expert Comment

by:athapa
ID: 21922280
I forgot to paste the link in the last post.
Anyway, check this link.
http://www.codeproject.com/KB/dotnet/bridge.aspx

Depending upon the .net framework you're using c++/clr wrapper would be slightly different. The new framework (2.0+) uses slightly different syntax (such as difference in ~ and ! and a new ^, etc.) . One of the comment in the above article also takes a bit about the differences.

Can you post your wrapper code or section of it?

0
 
LVL 1

Author Comment

by:m8online
ID: 21923511
I did post the code which I had earlier in this topic. But I don't think it will tell you a whole lot because the only actual piece of code which I added myself is the declaration of an object which should be taken from the managed class library.

Meanwhile I have been doing some more experimenting and managed to at least get a managed C++ application to compile in such a way that the imported class got recognized. I was hoping that I could convert this to a DLL project later on.

The problem I am having now, is that I am not allowed to declare a managed object as global.

In the bridge example (http://www.codeproject.com/KB/dotnet/bridge.aspx)  the bridging functions create an instance of the managed class each time when they are called. Then when the bridge function returns the object is disposed.

I cannot use this principle for my class as it also has data members which need to be retained. Therefore I would need to create an instance (object) of the managed class when calling some init function and do not dispose it before the application ends.
The most logical way I could think of to do this would be to declare a global pointer to an object from the managed class library. Which is not allowed.
Is this possible in some other way?
0
 
LVL 1

Author Comment

by:m8online
ID: 21924129
By the way: @athapa.

I just ordered the book you mentioned. It looks very useful. Thanks!
0
 
LVL 10

Expert Comment

by:athapa
ID: 22039142
You may have already resolved your issues. Anyway, I tested a simple program where a c# class is wrapped in c++ managed class. The c++ managed class is then accessed using loadlibrary in another pure c++ application. I was able to call the managed functions multiple times and retain the state (i.e. I didn't have to recreate managed object in every function calls).  In the c++ bridge, I added a static managed object. But you need to make sure you don't instantiate the managed object inside constructor or DLL_PROCESS_ATTACH/DLL_THREAD_ATTACH in DllMain.
0
 
LVL 1

Author Comment

by:m8online
ID: 22041805
No, in fact I am still working on it. I though I should read some chapters of the book before continuing. I just got it last week.

 Could I take a look at your source code?

I managed to get a static instance of the class using the GCroot. Now I am kind of stuck on how to connect the callback functions. The problem here is that I would want to create objects inside the wrapper handling the callbacks from each object in the managed world. Only you cannot use a method as a callbackfunction directly.
I took a look at functors, but they need such complicated code that I do not know if I will want to use those.
0
 
LVL 10

Accepted Solution

by:
athapa earned 250 total points
ID: 22051070
not the best possible code but it works.

managed.dll is written in c#
bridge.dll is written in c++ managed/unmanaged which wraps managed.dll
test.exe is written in c++ unmanaged which uses bridge.dll (class written in c# indirectly).

//BEGIN----------------------------managed.dll-------------------------
 
//managed.cs
//managed class in c#
using System;
using System.Collections.Generic;
using System.Text;
 
namespace managed
{
    public class test
    {
        private static test instance = null;
        private int value1 = 0;
 
        public test()
        {
            return;
        }
        
        public void Method1(int val)
        {
            value1 += val;
        }
        public int Method2()
        {
            return value1;
        }
    }
}
 
//END----------------------------managed.dll-------------------------
 
//BEGIN--------------------------bridge.dll--------------------------
 
// stdafx.h
//-------------
#include "gcroot.h"
#using <mscorlib.dll>
#using <managed.dll>
#pragma once
 
#include <windows.h>
//----------------
 
//stdafx.cpp
//----------------
#include "stdafx.h"
//----------------
 
//managed_bridge.h
//----------------
#include "gcroot.h"
#ifdef BRIDGE_EXPORTS
#define BRIDGE_API __declspec(dllexport)
#else
#define BRIDGE_API __declspec(dllimport)
#endif
 
class BRIDGE_API Cbridge {
private:
	//
	gcroot<managed::test^> t;
public:
	Cbridge(void);
	~Cbridge(void);
	bool Initialized;
	void fnInitialize(void);
	void fnMethod1(int val1);
    int fnMethod2(void);
};
//----------------
 
//managed_bridge.cpp
//----------------
#include "stdafx.h"
#include "managed_bridge.h"
 
void Cbridge::fnInitialize(void)
{
	t = gcnew managed::test();
	Initialized = true;
}
 
int Cbridge::fnMethod2(void)
{
	return t->Method2();
}
 
void Cbridge::fnMethod1(int val1)
{
	t->Method1(val1);
}
 
Cbridge::Cbridge(void)
{
	Initialized = false;
	return;
}
 
Cbridge::~Cbridge()
{
	delete t;
	return;
}
//----------------
 
//unmanaged_bridge.h
//----------------
#include "managed_bridge.h"
#pragma unmanaged
 
static Cbridge* cb;
static bool Initialized;
static void fnInitializeIfNeeded(void);
//----------------
 
//unmanaged_bridge.cpp
//----------------
#include "stdafx.h"
#include "unmanaged_bridge.h"
 
#ifdef _MANAGED
#pragma managed(push, off)
#endif
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		//DONOT CREATE ANY MANAGED INSTANCES HERE
		//if (cb == NULL)
		//	cb = new Cbridge();
	case DLL_THREAD_ATTACH:
		//DONOT CREATE ANY MANAGED INSTANCES HERE
		//if (cb == NULL)
		//	cb = new Cbridge();
	case DLL_THREAD_DETACH:
		/*if (cb != NULL)
			delete cb;*/
	case DLL_PROCESS_DETACH:
		//if (cb != NULL)
		//	delete cb;
		break;
	}
    return TRUE;
}
 
#ifdef _MANAGED
#pragma managed(pop)
#endif
 
extern "C" int WINAPI fnMethod2(void)
{
	fnInitializeIfNeeded();
	return cb->fnMethod2();
}
 
extern "C" void WINAPI fnMethod1(int val1)
{
	fnInitializeIfNeeded();
	cb->fnMethod1(val1);
}
 
 
static void fnInitializeIfNeeded(void)
{
	if (!Initialized) 
	{
		if (cb == NULL)
			cb = new Cbridge();
		cb->fnInitialize();
		Initialized = true;
	}
	return;
}
//----------------
 
//END----------------------------bridge.dll--------------------------
 
//BEGIN--------------------------test.exe----------------------------
//Pure unmanaged class which will use the bridge
 
//stdafx.h
//----------------
#pragma once
//----------------
 
//stdafx.cpp
//----------------
#include "stdafx.h"
//----------------
 
//test.cpp
//----------------
#include "stdafx.h"
 
#include <afx.h>
#include <afxwin.h> 
#include <afxext.h> 
 
#include <windows.h>
 
#include <string>
#include <iostream>
 
using namespace std;
 
typedef void (WINAPI *fnMethod1)(int val1);
typedef int (WINAPI *fnMethod2)(void);
 
int _tmain(int argc, _TCHAR* argv[])
{
	USES_CONVERSION;
 
	HMODULE m;
 
	MessageBox(NULL,_T("Loading library"),_T("Loading"),0);
 
	m = LoadLibrary(_T("bridge.dll"));
	if (m != NULL)
	{
		fnMethod1 fnM1 = (fnMethod1) GetProcAddress (m, "fnMethod1");
		if (fnM1 != NULL)
		{
			fnMethod2 fnM2 = (fnMethod2) GetProcAddress (m, "fnMethod2");
			if (fnM2 != NULL)
			{
				//test the functions
 
				int val1=0;				
				char *val2;
				wchar_t *val3;
				int len;
 
				val2 = new char[20];
 
				//call the functions few times
				fnM1(50);
				val1 = fnM2();
				//first time the value returned from fnM2 is 50
				itoa(val1,val2,10);
				val3 = A2W(val2);
				MessageBox(NULL,val3,_T("Val1"),0);
 
 
				fnM1(20);
				val1 = fnM2();
				//second time the value returned from fnM2 is 70 (50+20)
				//so the object is presisting beyond one call
				itoa(val1,val2,10);
				val3 = A2W(val2);
				MessageBox(NULL,val3,_T("Val1"),0);
				
				delete val2;
			}
		}
		FreeLibrary(m);
	}
	return 0;
}
//----------------
 
//END----------------------------test.exe----------------------------

Open in new window

0
 
LVL 1

Author Comment

by:m8online
ID: 22202295
Hello All,

Just to let you all know.
I am still working on this, but time pressure on some other project forces me to put it on hold regularly.
I will keep you informed on any new progress.
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
The viewer will learn how to use NetBeans IDE 8.0 for Windows to connect to a MySQL database. Open Services Panel: Create a new connection using New Connection Wizard: Create a test database called eetutorial: Create a new test tabel called ee…
The viewer will learn how to synchronize PHP projects with a remote server in NetBeans IDE 8.0 for Windows.

752 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question