Hello all.
Firstly, Thanks Byron for your reply. It confirmed what I thought. However I think it is still useful to access DOT.NET from FWH and so the following .....
Since I last posted this question on this forum for access to MS .NET from FiveWin, I have been working on it. I have established a way that works for me. It is not elegant. It requires a few steps, but it works for me. I would like to return some hopefully useful information to the FiveWin community as all of you have help me a lot in solving and getting new ideas/approaches for my application. In sharing my approach with everyone, I hope that it too could be useful to you. I specifically asked for "SerialPort" help since I am writing a custom hosting interface between a PC and a USB device (in my case, a Bar Code Reader ... essentially a USB interface is a serial port ... see:
http://msdn.microsoft.com/en-us/library/aa908437.aspx ).
I use Windows XP Pro. I have download FWH 9.06 and use the supplied xHarbour and Borlandc c/c++ compiler for all my "normal" application developement. This approach should work for previous versions of FWH. For the DOT.NET interface, I use the supplied Harbour/[x]Harbour MS libraries and headers with Visual Studio 2008 c/c++ compiler and linker.
I apoligise for the length of the post but I think the detailed information would be useful to someone.
------------------------------------------------------------------------------------------------------------
In General, the step taken are as follows:
(1) Write a c/c++ wrapper that interfaces with Harbour/[x]Harbour.
I called this module: XBase_IF.cpp
This takes care of the communication between FWH source and low level c/c++ code.
Use the FWH supplied Borland C++ V5.5.1 for Win32 compiler/linker on this module.
(2) Use a MS compiler/linker that *suports* DOT.NET. Earlier versions of Visual c/c++
do not support it. (here is where you write your c/c++ code to communicate with
DOT.NET namespaces and classes
I called this module: cfuncs_vs.cpp
(3) Write your cfuncs_vs.cpp "DOT.NET interface c/c++" code that accesses DOT.NET and
compile with the /clr compiler option.
Use Multi-threaded DLL (/MD) for the runtime library.
Ensure your targetted Framework is .NET Framework V2.0 or above.
Set to generate a DLL (/DLL linker option). I generate a STATIC DLL which means I
link in an import library with the FWH code. I think this is the easiest and safest
way to interface with my FWH code.
(4) Now generate an Import Library from the generated DLL file suitable to link in with FWH.
Use the impdef and implib routines to generate an OMF formatted library from the DLL that
lists/defines all the exported functions in the DLL. Execute the following
impdef cfuncs_vs.def cfuncs_vs.dll
implib cfuncs_vs.lib cfuncs_vs.def
(5) Include "cfuncs_vs.lib" in your make/build file that generates your FWH application
(6) If all goes well there are no unresolved symbols and an .exe file is generated.
(7) That's it. Hopefully you made sense of "this general recipe". In any case I have included
a cut down version showing all the above steps that actually compiles and links on my system
--------------------------------------------------------------------------------
Step (1) Harbour/[x]Harbour c/c++ wrapper source code
- Code: Select all Expand view
// ===========================================================================
// XBase_IF.cpp July 2009 Angelo C
// ============================1===============================================
#include <winten.h> // More includes than necessary for this cut down version.
#include <stdio.h> // My complete code needs
#include <Winsock2.h>
#include <stdlib.h>
#include <malloc.h>
#include <lm.h>
#include <Icmpapi.h>
#include <Windows.h>
#include <psapi.h>
#include "hbapi.h" //Need so that FWH can pass parameters back/forth
#include "hbapirdd.h" //btween XBase and c/c++ code
#include "hbapierr.h"
#include "hbapiitm.h"
#include "hbvm.h"
#include "hbset.h"
#ifdef __cplusplus
extern "C" {
#endif
// Prototypes for function in the cfuncs_vs DLL. Also used for accessing DOT.NET framework
extern int TEST_NAMESPACE_in_DLL();
//This routine is called from FWH as: TEST_NAMESPACE()
HB_FUNC( TEST_NAMESPACE )
{
int nResult;
OutputDebugString_c("\nIn routine: HB_FUNC( TEST_NAMESPACE ).\n");
nResult = TEST_NAMESPACE_in_DLL();
OutputDebugString_c(Int2Hex((unsigned int) nResult));
}
#ifdef __cplusplus
}
#endif
Part output from my make file is below:
MAKE Version 5.2 Copyright (c) 1987, 2000 Borland
C:\Borland\BCC55\bin\bcc32 -c -oXBase_IF.obj -DHB_DBG_TRACE
-ID:\FIVETECH\FWH\include;D:\FIVETECH\xharbour\include;C:\Borland\BCC55\Include;D:\ac\fwhdb_phx\Inc;D:\ac\fwhdb_phx\Source\c_src XBase_IF.cpp
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
Xbase_IF.cpp:
C:\Borland\BCC55\bin\bcc32 -c -ocfuncs.obj -DHB_DBG_TRACE -ID:\FIVETECH\FWH\include;D:\FIVETECH\xharbour\include;C:\Borland\BCC55\Include;D:\ac\fwhdb_phx\Inc;D:\ac\fwhdb_phx\Source\c_src cfuncs.cpp
--------------------------------------------------------------------------------
Steps (2) and (3) Write your cfuncs_vs.cpp "DOT.NET interface c/c++" code
stdafx.h : include file follows:
- Code: Select all Expand view
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
// The following macros define the minimum required platform. The minimum required platform
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
// your application. The macros work by enabling all features available on platform versions up to and
// including the version specified.
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef WINVER // Specifies that the minimum required platform is Windows Vista.
#define WINVER 0x0600 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif
#ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 7.0.
#define _WIN32_IE 0x0700 // Change this to the appropriate value to target other versions of IE.
#endif
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// TODO: reference additional headers your program requires here
--------------------------------------------------------------------
[
dllmain.cpp Source Follows:
- Code: Select all Expand view
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE 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;
}
--------------------------------------------------------------------
cfuncs_vs.cpp "DOT.NET interface c/c++" Module follows:
- Code: Select all Expand view
// ===========================================================================
// c_funcs.cpp July 2009 Angelo C.
// ============================1===============================================
// cfuncs_vs.cpp : Defines the exported functions for the DLL application.
// This is CALLED from other C/C++ modules which can be compiled by Borlandc
// which interfaces with FWH/[x]Harbour source directly using hb_parYYYY, etc functions
// NOTE. Function names that are called from the Borlandc c/c++ wrapper routine MUST HAVE
// an underscore "_" included at the start of the name.
// Borlandc puts this in automatically.
// SUMMARY. Put "_" at start of routines called from Borlandc Wrapper module
#include "stdafx.h"
#define CFUNCS_VS_API __declspec(dllexport)
using namespace System;
// Routine converts system strings to another type
// 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));
}
//----------------------------------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
#using <System.dll>
//A Test for this namespace
CFUNCS_VS_API int _TEST_NAMESPACE_in_DLL()
{
string a = "test";
OutputDebugStringA("\nIn (TEST_NAMESPACE_in_DLL(...): ");
using namespace System;
using namespace System::IO::Ports;
using namespace System::ComponentModel;
array<String^>^ serialPorts = nullptr;
try
{
// Get a list of serial port names.
serialPorts = SerialPort::GetPortNames();
}
catch (Win32Exception^ ex)
{
OutputDebugStringA("\nError: ex->Message: ");
MarshalString(ex->Message, a);
OutputDebugStringA(a.c_str());
}
OutputDebugStringA("\nThe following serial ports were found:");
// Display each port name to the console.
for each(String^ port in serialPorts)
{
MarshalString(port, a);
OutputDebugStringA(a.c_str());
}
}
//Function prototype descriptions for Windows API functions
// lpOutputString is a pointer to string to be displayed in debug window
WINBASEAPI VOID WINAPI OutputDebugStringA( LPCSTR lpOutputString );
// Converts an integer (2 or 4 bytes) into a hex string ( 4 or 8 chars)
CFUNCS_VS_API char * _Int2Hex_dll( unsigned int wWord )
{
static far char sIntHex[ 17 ];
WORD i;
if (sizeof(int) == 4) //Platform has 4 byte represenattion for int
{
i = 9;
do
{
sIntHex[ i ] = 48 + ( wWord & 0x000000000000000F );
if( sIntHex[ i ] > 57 )
sIntHex[ i ] += 7;
wWord >>= 4;
} while( i-- > 0 );
sIntHex[ 16 ] = 0;
}
else //Platform has 2 byte represenattion for int
{
i= 3;
do
{
sIntHex[ i ] = 48 + ( wWord & 0x000F );
if( sIntHex[ i ] > 57 )
sIntHex[ i ] += 7;
wWord >>= 4;
} while( i-- > 0 );
sIntHex[ 4 ] = 0;
}
return sIntHex;
}
/* ============================================================================
* OutputDebugStringCR_DLL( cStr )
*
* Purpose:
* Send a string the the system debugger window WITH <CR>+<LF> printed first
* ========================================================================= */
CFUNCS_VS_API void _OutputDebugStringCR_DLL(char * cMsg) {
OutputDebugStringA("\n");
OutputDebugStringA(cMsg);
}
#ifdef __cplusplus
}
#endif
Build Log Output from Visual Studio 2008:
Build Log Rebuild started: Project: cfuncs_vs, Configuration: Release|Win32
Command Lines Creating temporary file "d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002942085648.rsp" with contents
[
/O2 /Oi /GL /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "CFUNCS_VS_EXPORTS" /D "_WINDLL" /D "_UNICODE" /D "UNICODE" /FD /EHa /MD /Gy
/Yu"stdafx.h" /Fp"Release\cfuncs_vs.pch" /Fo"Release\\" /Fd"Release\vc90.pdb" /W3 /c /Zi /clr /TP .\cfuncs_vs.cpp
]
Creating command line "cl.exe @d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002942085648.rsp /nologo /errorReport:prompt"
Creating temporary file "d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002A42085648.rsp" with contents
[
/O2 /Oi /GL /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "CFUNCS_VS_EXPORTS" /D "_WINDLL" /D "_UNICODE" /D "UNICODE" /FD /EHa /MD /Gy /Yc"stdafx.h" /Fp"Release\cfuncs_vs.pch" /Fo"Release\\" /Fd"Release\vc90.pdb" /W3 /c /Zi /clr /TP .\stdafx.cpp
]
Creating command line "cl.exe @d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002A42085648.rsp /nologo /errorReport:prompt"
Creating temporary file "d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002B42085648.rsp" with contents
[
/O2 /Oi /GL /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "CFUNCS_VS_EXPORTS" /D "_WINDLL" /D "_UNICODE" /D "UNICODE" /FD /EHa /MD /Gy /Fo"Release\\" /Fd"Release\vc90.pdb" /W3 /c /Zi /TP .\dllmain.cpp
]
Creating command line "cl.exe @d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002B42085648.rsp /nologo /errorReport:prompt"
Creating temporary file "d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002C42085648.rsp" with contents
[
/OUT:"D:\ac\fwhdb_phx\cfuncs_vs\Release\cfuncs_vs.dll" /INCREMENTAL:NO /DLL /MANIFEST
/MANIFESTFILE:"Release\cfuncs_vs.dll.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /LTCG /DYNAMICBASE /FIXED:No /NXCOMPAT /MACHINE:X86 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib
shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib
".\Release\cfuncs_vs.obj"
".\Release\dllmain.obj"
".\Release\stdafx.obj"
]
Creating command line "link.exe @d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002C42085648.rsp /NOLOGO /ERRORREPORT:PROMPT"
Creating temporary file "d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002D42085648.rsp" with contents
[
/outputresource:"..\Release\cfuncs_vs.dll;#2" /manifest
.\Release\cfuncs_vs.dll.intermediate.manifest
]
Creating command line "mt.exe @d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\RSP00002D42085648.rsp /nologo"
Creating temporary file "d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\BAT00002E42085648.bat" with contents
[
@echo Manifest resource last updated at %TIME% on %DATE% > .\Release\mt.dep
]
Creating command line "d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\BAT00002E42085648.bat"
Output Window Compiling...
stdafx.cpp
Compiling...
cfuncs_vs.cpp
Compiling...
dllmain.cpp
Linking...
Creating library D:\ac\fwhdb_phx\cfuncs_vs\Release\cfuncs_vs.lib and object D:\ac\fwhdb_phx\cfuncs_vs\Release\cfuncs_vs.exp
Generating code
Finished generating code
Embedding manifest...
Results Build log was saved at "file://d:\ac\fwhdb_phx\cfuncs_vs\cfuncs_vs\Release\BuildLog.htm"
cfuncs_vs - 0 error(s), 0 warning(s)
--------------------------------------------------------------------------------
Step(4)
Result from impdef and implib calls.
D:\ac\fwhdb_phx\LIB>impdef cfuncs_vs.def cfuncs_vs.dll
CodeGear Impdef Version 3.1.0 Copyright (c) 1991-2007 CodeGear
D:\ac\fwhdb_phx\LIB>implib cfuncs_vs.lib cfuncs_vs.def
CodeGear Implib Version 3.1.0 Copyright (c) 1991-2007 CodeGear
Name: 'cfuncs_vs' Ext: '.dll' Base: 0x00000000
Name: 'CFUNCS_VS.DLL' Ext: '.dll' Base: 0x00000000
--------------------------------------------------------------------------------
Step(5)
Output from my make file:
MAKE Version 5.2 Copyright (c) 1987, 2000 Borland
echo C:\Borland\BCC55\lib\c0w32.obj + > b32.bc
echo ph_win32.obj, + >> b32.bc
echo ph_win32.exe, + >> b32.bc
echo ph_win32.map, + >> b32.bc
echo D:\ac\fwhdb_phx\lib\cfuncs_vs.lib + >> b32.bc
echo D:\FiveTech\FWH\lib\FiveHx.lib D:\FiveTech\FWH\lib\FiveHC.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\rtl.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\vm.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\pp.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\rdd.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\dbfntx.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\dbfcdx.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\gtgui.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\lang.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\macro.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\dbffpt.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\hbsix.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\debug.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\common.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\pcrepos.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\ct.lib + >> b32.bc
echo C:\Borland\BCC55\lib\cw32mt.lib + >> b32.bc
echo C:\Borland\BCC55\lib\import32.lib + >> b32.bc
echo C:\Borland\BCC55\lib\uuid.lib + >> b32.bc
echo C:\Borland\BCC55\lib\psdk\odbc32.lib + >> b32.bc
echo C:\Borland\BCC55\lib\psdk\rasapi32.lib + >> b32.bc
echo C:\Borland\BCC55\lib\psdk\nddeapi.lib + >> b32.bc
echo C:\Borland\BCC55\lib\psdk\msimg32.lib + >> b32.bc
echo C:\Borland\BCC55\lib\psdk\psapi.lib + >> b32.bc
echo D:\FiveTech\xHarbour\lib\usrrdd.lib + >> b32.bc
echo D:\ac\fwhdb_phx\lib\sql.lib + >> b32.bc
echo D:\ac\fwhdb_phx\lib\odbccp32.lib + >> b32.bc
echo D:\ac\fwhdb_phx\lib\sqlmt.lib + >> b32.bc
echo D:\ac\fwhdb_phx\lib\IPHlpApi.Lib, >> b32.bc
echo D:\ac\fwhdb_phx\Resource\ph_win32.res >> b32.bc
C:\Borland\BCC55\bin\ilink32 -LC:\Borland\BCC55\Lib -Gn -aa -Tpe -s -v @b32.bc
Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
--------------------------------------------------------------------------------
Step(6)
Debug Ouput (in the debug window) from my call to TEST_NAMESPACE()
In routine: HB_FUNC( TEST_NAMESPACE ).
In (TEST_NAMESPACE_in_DLL(...):
The following serial ports were found:COM3
Returned test result from TEST_NAMESPACE in a DLL is:
0000000038 <- this is in hex => 56 Decimal
[/list][/list]
--------------------------------------------------------------------------------
There you have it.
Best Regards,
Angelo.c