Topaz Filer: if you use e-mail for business, we can save you money and decrease your risk.
Structured Storage with .NET
Messages   Related Types
This message was discovered on microsoft.public.dotnet.framework.interop.


Marco Filippini
Hi,

I'm trying to use OLE Structured Storage in .NET.
Since there is no native support into the framework, the hard task is to
implements interfaces and ole dll functions with interop.
Does anybody tried the same path with success and have some code to share
(VB.NET should be better, but C# is fine)?

Thx in advance, Marco

Reply to this message...
Vote that this is a GOOD answer...
 
Auto-following on Twitter
Ubuntu and XP on one “desktop”
 
    
Nicholas Paldino [.NET/C# MVP]
Marco,

It shouldn't be too hard to declare the interfaces yourself and the
functions in the DLL's that you require. Is there a specific
interface/function you are having problems with?

--
- Nicholas Paldino [.NET MVP]
- Click here to reveal e-mail address

"Marco Filippini" <Click here to reveal e-mail address> wrote in message
news:uHmYAaU5BHA.568@tkmsftngp05...
[Original message clipped]

Reply to this message...
Vote that this is a GOOD answer...
 
Outlook interop - stopping user properties appearing on Outlook message print
Seriously, why is “cut and paste” majorly newsworthy???
 
    
Marco Filippini
Here are the definitions:

------------------------------------------------------ Begin
Definitions ----------------------------------------------------------------
---------
Public Const cstFMTIDSummaryInformation As String =
"{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"

Public Enum STGM As Integer
STGM_READ = 0
STGM_WRITE = 1
STGM_READWRITE = 2
STGM_SHARE_DENY_NONE = &H40
STGM_SHARE_DENY_READ = &H30
STGM_SHARE_DENY_WRITE = &H20
STGM_SHARE_EXCLUSIVE = &H10
STGM_PRIORITY = &H40000
STGM_CREATE = &H1000
STGM_CONVERT = &H20000
STGM_FAILIFTHERE = 0
STGM_DIRECT = 0
STGM_TRANSACTED = &H10000
STGM_NOSCRATCH = &H100000
STGM_NOSNAPSHOT = &H200000
STGM_SIMPLE = &H8000000
STGM_DIRECT_SWMR = &H400000
STGM_DELETEONRELEASE = &H4000000
End Enum

Public Enum STGC As Integer
STGC_DEFAULT = 0
STGC_OVERWRITE = 1
STGC_ONLYIFCURRENT = 2
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4
STGC_CONSOLIDATE = 8
End Enum

Public Enum STREAM_SEEK As Integer
STREAM_SEEK_SET = 0
STREAM_SEEK_CUR = 1
STREAM_SEEK_END = 2
End Enum

Public Enum STATFLAG As Integer
STATFLAG_DEFAULT = 0
STATFLAG_NONAME = 1
End Enum

Public Enum STGMOVE As Integer
STGMOVE_MOVE = 0
STGMOVE_COPY = 1
End Enum

Public Enum PRPSPEC As Integer
PRSPEC_LPWSTR = 0
PRSPEC_PROPID = 1
End Enum

Public Enum PROPSETFLAG As Integer
PROPSETFLAG_DEFAULT = 0
PROPSETFLAG_NONSIMPLE = 1
PROPSETFLAG_ANSI = 2
PROPSETFLAG_UNBUFFERED = 4
PROPSETFLAG_CASE_SENSITIVE = 8
End Enum

Public Enum SumInfoPIDEnum As Integer
PIDSI_TITLE = 2
PIDSI_SUBJECT = 3
PIDSI_AUTHOR = 4
PIDSI_KEYWORDS = 5
PIDSI_COMMENTS = 6
PIDSI_TEMPLATE = 7
PIDSI_LASTAUTHOR = 8
PIDSI_REVNUMBER = 9
PIDSI_EDITTIME = &HA
PIDSI_LASTPRINTED = &HB
PIDSI_CREATE_DTM = &HC
PIDSI_LASTSAVE_DTM = &HD
PIDSI_PAGECOUNT = &HE
PIDSI_WORDCOUNT = &HF
PIDSI_CHARCOUNT = &H10
PIDSI_THUMBNAIL = &H11
PIDSI_APPNAME = &H12
PIDSI_SECURITY = &H13
End Enum

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)> _
Public Structure LPGUID
Public Data1 As Integer
Public Data2 As Short
Public Data3 As Short
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> Public Data4()
As Byte
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)> _
Public Structure ULARGE_INTEGER
Public LowPart As Integer
Public HighPart As Integer
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)> _
Public Structure PROPSPEC_INT
Public ulKind As PRPSPEC
Public propid As Integer
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)> _
Public Structure PROPSPEC_STR
Public ulKind As PRPSPEC
<MarshalAs(UnmanagedType.LPWStr)> Public lpwstr As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)> _
Public Structure STATPROPSETSTG
Public fmtid As LPGUID
Public clsid As LPGUID
Public grfFlags As Integer
Public mtime As FILETIME
Public ctime As FILETIME
Public atime As FILETIME
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)> _
Public Structure STATPROPSTG
<MarshalAs(UnmanagedType.LPWStr)> Public lpwstrName As String
Public propid As Integer
Public vt As Short
End Structure

<Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComConversionLoss()> _
Public Interface ISequentialStream
Function Read(ByVal pv As IntPtr, ByVal cb As Integer, ByRef pcbRead
As Integer) As Integer
Function Write(ByVal pv As IntPtr, ByVal cb As Integer, ByRef
pcbWritten As Integer) As Integer
End Interface

<Guid("0000000C-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComConversionLoss()> _
Public Interface IStream : Inherits ISequentialStream
Function Clone(ByRef ppstm As IStream) As Integer
Function Commit(ByVal grfCommitFlags As STGC) As Integer
Function CopyTo(ByVal pstm As IStream, ByVal cb As ULARGE_INTEGER,
ByRef pcbRead As ULARGE_INTEGER, ByRef pcbWritten As ULARGE_INTEGER) As
Integer
Function LockRegion(ByVal libOffset As ULARGE_INTEGER, ByVal cb As
ULARGE_INTEGER, ByVal dwLockType As Integer) As Integer
Function Revert() As Integer
Function Seek(ByVal dlibMove As ULARGE_INTEGER, ByVal dwOrigin As
STREAM_SEEK, ByRef plibNewPosition As ULARGE_INTEGER) As Integer
Function SetSize(ByVal libNewSize As ULARGE_INTEGER) As Integer
Function Stat(ByRef pstatstg As STATSTG, ByVal grfStatFlag As
STATFLAG) As Integer
Function UnlockRegion(ByVal libOffset As ULARGE_INTEGER, ByVal cb As
ULARGE_INTEGER, ByVal dwLockType As Integer) As Integer
End Interface

<Guid("0000000B-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComConversionLoss()> _
Public Interface IStorage
Function Commit(ByVal grfCommitFlags As STGC) As Integer
Function CopyTo(ByVal ciidExclude As Integer, ByVal rgiidExclude As
LPGUID, ByVal snbExclude As IntPtr, ByVal pstgDest As IStorage) As Integer
Function CreateStorage(<MarshalAs(UnmanagedType.LPWStr)> ByVal
pwcsName As String, ByVal grfMode As STGM, ByVal reserved1 As Integer, ByVal
reserved2 As Integer, ByRef ppstg As IStorage) As Integer
Function CreateStream(<MarshalAs(UnmanagedType.LPWStr)> ByVal
pwcsName As String, ByVal grfMode As STGM, ByVal reserved1 As Integer, ByVal
reserved2 As Integer, ByRef ppstm As IStream) As Integer
Function DestroyElement(<MarshalAs(UnmanagedType.LPWStr)> ByVal
pwcsName As String) As Integer
Function EnumElements(ByVal reserved1 As Integer, ByVal reserved2 As
IntPtr, ByVal reserved3 As Integer, ByRef ppenum As IEnumSTATSTG) As Integer
Function MoveElementTo(<MarshalAs(UnmanagedType.LPWStr)> ByVal
pwcsName As String, ByVal pstgDest As IStorage,
<MarshalAs(UnmanagedType.LPWStr)> ByVal pwcsNewName As String, ByVal
grfFlags As STGMOVE) As Integer
Function OpenStorage(<MarshalAs(UnmanagedType.LPWStr)> ByVal
pwcsName As String, ByVal pstgPriority As IntPtr, ByVal grfMode As STGM,
ByVal snbExclude As IntPtr, ByVal reserved As Integer, ByRef ppstg As
IStorage) As Integer
Function OpenStream(<MarshalAs(UnmanagedType.LPWStr)> ByVal pwcsName
As String, ByVal reserved1 As IntPtr, ByVal grfMode As STGM, ByVal reserved2
As Integer, ByRef ppstm As IStream) As Integer
Function RenameElement(<MarshalAs(UnmanagedType.LPWStr)> ByVal
pwcsOldName As String, <MarshalAs(UnmanagedType.LPWStr)> ByVal pwcsNewName
As String) As Integer
Function Revert() As Integer
Function SetClass(ByVal clsid As LPGUID) As Integer
Function SetElementTimes(<MarshalAs(UnmanagedType.LPWStr)> ByVal
pwcsName As String, ByVal pctime As FILETIME, ByVal patime As FILETIME,
ByVal pmtime As FILETIME) As Integer
Function SetStateBits(ByVal grfStateBits As Integer, ByVal grfMask
As Integer) As Integer
Function Stat(ByRef pstatstg As STATSTG, ByVal grfStatFlag As
STATFLAG) As Integer
End Interface

<Guid("0000000D-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IEnumSTATSTG
Function Clone(ByRef ppenum As IEnumSTATSTG) As Integer
Function [Next](ByVal celt As Integer, ByRef rgelt As STATSTG, ByRef
pceltFetched As Integer) As Integer
Function Reset() As Integer
Function Skip(ByVal celt As Integer) As Integer
End Interface

<Guid("00000138-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComConversionLoss()> _
Public Interface IPropertyStorage
Function Commit(ByVal grfCommitFlags As STGC) As Integer
Overloads Function DeleteMultiple(ByVal cpspec As Integer, ByVal
rgpspec() As PROPSPEC_INT) As Integer
Overloads Function DeleteMultiple(ByVal cpspec As Integer, ByVal
rgpspec() As PROPSPEC_STR) As Integer
Function DeletePropertyNames(ByVal cpropid As Integer, ByVal
rgpropid() As Integer) As Integer
Function [Enum](ByRef ppenum As IEnumSTATPROPSTG) As Integer
Overloads Function ReadMultiple(ByVal cpspec As Integer, ByVal
rgpspec() As PROPSPEC_INT, ByRef rgvar() As Object) As Integer
Overloads Function ReadMultiple(ByVal cpspec As Integer, ByVal
rgpspec() As PROPSPEC_STR, ByRef rgvar() As Object) As Integer
Function ReadPropertyNames(ByVal cpropid As Integer, ByVal
rgpropid() As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal
rglpwstrName() As String) As Integer
Function Revert() As Integer
Function SetClass(ByVal clsid As LPGUID) As Integer
Function SetTimes(ByVal pctime As FILETIME, ByVal patime As
FILETIME, ByVal pmtime As FILETIME) As Integer
Function Stat(ByRef pstatpsstg As STATPROPSETSTG) As Integer
Overloads Function WriteMultiple(ByVal cpspec As Integer, ByVal
rgpspec() As PROPSPEC_INT, ByVal rgvar() As Object, ByVal propidNameFirst As
Integer) As Integer
Overloads Function WriteMultiple(ByVal cpspec As Integer, ByVal
rgpspec() As PROPSPEC_STR, ByVal rgvar() As Object, ByVal propidNameFirst As
Integer) As Integer
Function WritePropertyNames(ByVal cpropid As Integer, ByVal
rgpropid() As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal
rglpwstrName() As String) As Integer
End Interface

<Guid("0000013A-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown), ComConversionLoss()> _
Public Interface IPropertySetStorage
Function Create(ByVal fmtid As LPGUID, ByVal pclsid As LPGUID, ByVal
grfFlags As PROPSETFLAG, ByVal grfMode As STGM, ByRef ppPropStg As
IPropertyStorage) As Integer
Function Delete(ByVal fmtid As LPGUID) As Integer
Function [Enum](ByRef ppenum As IEnumSTATPROPSETSTG) As Integer
Function Open(ByVal fmtid As LPGUID, ByVal grfMode As STGM, ByRef
ppPropStg As IPropertyStorage) As Integer
End Interface

<Guid("00000139-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IEnumSTATPROPSTG
Function Clone(ByRef ppenum As IEnumSTATPROPSTG) As Integer
Function [Next](ByVal celt As Integer, ByRef rgelt As STATPROPSTG,
ByRef pceltFetched As Integer) As Integer
Function Reset() As Integer
Function Skip(ByVal celt As Integer) As Integer
End Interface

<Guid("0000013B-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IEnumSTATPROPSETSTG
Function Clone(ByRef ppenum As IEnumSTATPROPSETSTG) As Integer
Function [Next](ByVal celt As Integer, ByRef rgelt As
STATPROPSETSTG, ByRef pceltFetched As Integer) As Integer
Function Reset() As Integer
Function Skip(ByVal celt As Integer) As Integer
End Interface

<DllImport("ole32.dll", EntryPoint:="CLSIDFromString",
SetLastError:=True, CharSet:=CharSet.Auto,
CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function CLSIDFromString(<MarshalAs(UnmanagedType.LPWStr)>
ByVal pwcsName As String, ByRef pclsid As LPGUID) As Integer
End Function

<DllImport("ole32.dll", EntryPoint:="StgIsStorageFile",
SetLastError:=True, CharSet:=CharSet.Auto,
CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function
StgIsStorageFile(<MarshalAs(UnmanagedType.LPWStr)> ByVal pwcsName As String)
As Integer
End Function

<DllImport("ole32.dll", EntryPoint:="StgOpenStorage",
SetLastError:=True, CharSet:=CharSet.Auto,
CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function StgOpenStorage(<MarshalAs(UnmanagedType.LPWStr)>
ByVal pwcsName As String, ByVal pstgPriority As IntPtr, ByVal grfMode As
STGM, ByVal snbExclude As IntPtr, ByVal reserved As Integer, ByRef ppstgOpen
As IStorage) As Integer
End Function
------------------------------------------------------ End
Definitions ----------------------------------------------------------------
---------

Enumerations, structures and interfaces have been defined according to the
MSDN specifications, but, since I'm not so skilled, I could have done lotta
errors so if u checked out some, please let me know.

And here is the code:

------------------------------------------------------ Begin
Code -----------------------------------------------------------------------
--
Dim intReturnValue As Integer
Dim objIPropertyStorage As IPropertyStorage
Dim objIPropertySetStorage As IPropertySetStorage
Dim objIStorage As IStorage
Dim objOpenFileDialog As OpenFileDialog
Dim udtSummaryInformation As LPGUID
Dim objWin32Exception As Win32Exception
Dim udtPROPSPEC() As PROPSPEC_INT
Dim objValue As Object

Try
objOpenFileDialog = New OpenFileDialog()
objOpenFileDialog.ShowReadOnly = False
If objOpenFileDialog.ShowDialog() = DialogResult.OK Then
intReturnValue = StgIsStorageFile(objOpenFileDialog.FileName)
If intReturnValue <> 0 Then
objWin32Exception = New Win32Exception(intReturnValue)
ThrowException(objWin32Exception.Message, "StgIsStorageFile")
End If
intReturnValue = StgOpenStorage(objOpenFileDialog.FileName,
IntPtr.Zero, STGM.STGM_READWRITE Or STGM.STGM_SHARE_EXCLUSIVE, IntPtr.Zero,
0, objIStorage)
If intReturnValue <> 0 Then
objWin32Exception = New Win32Exception(intReturnValue)
ThrowException(objWin32Exception.Message, "StgOpenStorage")
End If
objIPropertySetStorage = CType(objIStorage, IPropertySetStorage)
intReturnValue = CLSIDFromString(cstFMTIDSummaryInformation,
udtSummaryInformation)
If intReturnValue <> 0 Then
objWin32Exception = New Win32Exception(intReturnValue)
ThrowException(objWin32Exception.Message,
"Invoke.CLSIDFromString")
End If
intReturnValue = objIPropertySetStorage.Open(udtSummaryInformation,
STGM.STGM_READWRITE Or STGM.STGM_SHARE_EXCLUSIVE, objIPropertyStorage)
If intReturnValue <> 0 Then
objWin32Exception = New Win32Exception(intReturnValue)
ThrowException(objWin32Exception.Message,
"IPropertySetStorage.Open")
End If
ReDim udtPROPSPEC(0)
udtPROPSPEC(0).ulKind = PRPSPEC.PRSPEC_PROPID
udtPROPSPEC(0).propid = SumInfoPIDEnum.PIDSI_AUTHOR
intReturnValue = objIPropertyStorage.ReadMultiple(1, udtPROPSPEC,
objValue)
If intReturnValue <> 0 Then
objWin32Exception = New Win32Exception(intReturnValue)
ThrowException(objWin32Exception.Message,
"IPropertyStorage.ReadMultiple")
End If
End If
Catch objException As Exception
Me.Cursor = Cursors.Default
MsgBox(objException.Message & " (" & objException.Source & ")")
Finally
objIPropertyStorage = Nothing
objIPropertySetStorage = Nothing
objIStorage = Nothing
objWin32Exception = Nothing
objOpenFileDialog.Dispose()
objOpenFileDialog = Nothing
End Try
------------------------------------------------------ End
Code -----------------------------------------------------------------------
--

When I try to call the Open method on IPropertySetStorage
(objIPropertySetStorage.Open), I get the following exception:

- objException {System.ArgumentException} System.Exception
+ [System.ArgumentException] {System.ArgumentException}
System.ArgumentException
Object {System.ArgumentException} Object
_COMPlusExceptionCode -532459699 Integer
_className Nothing String
_exceptionMethod Nothing System.Reflection.MethodBase
_exceptionMethodString Nothing String
_message "The parameter is incorrect." String
_innerException Nothing System.Exception
_helpURL Nothing String
+ _stackTrace {System.Array} Object
_stackTraceString Nothing String
_remoteStackTraceString Nothing String
_remoteStackIndex 0 Integer
_HResult -2147024809 Integer
_source "WindowsApplication4" String
_xptrs 0 System.IntPtr
_xcode -532459699 Integer
Message "The parameter is incorrect." String
InnerException Nothing System.Exception
+ TargetSite {System.Reflection.RuntimeMethodInfo}
System.Reflection.MethodBase
StackTrace " at WindowsApplication4.IPropertySetStorage.Open(LPGUID
fmtid, STGM grfMode, IPropertyStorage& ppPropStg)
at WindowsApplication4.Form1.Button1_Click(Object sender, EventArgs e) in
E:\Development\Visual Basic\DotNet\Windows
Application\WindowsApplication4\Form1.vb:line 316" String
HelpLink "" String
Source "WindowsApplication4" String
HResult -2147024809 Integer

What's wrong?

"Nicholas Paldino [.NET/C# MVP]" <Click here to reveal e-mail address> wrote
in message news:#hLLqnU5BHA.1632@tkmsftngp02...
[Original message clipped]

Reply to this message...
Vote that this is a GOOD answer...
 
 
    
Thomas Schmidt
Hi All,

I began porting the IStorage interface to a C# class but I'm having problems
with some attributes ([call_as(EnumElements)] for example) and typedefs. I
found no samples showing how to port these.

But anyway, it's the 2nd best solution to call COM only to access docfiles.
A plain .net class lib could be ported and would leave the RCW overhead
behind.

Thomas

"Nicholas Paldino [.NET/C# MVP]" <Click here to reveal e-mail address> wrote
in message news:#hLLqnU5BHA.1632@tkmsftngp02...
[Original message clipped]

Reply to this message...
Vote that this is a GOOD answer...
 
Email Archiving and Email Filing - what’s the difference?
Web-based task/todo list management
 
    
Mattias Sjögren
Thomas,

[Original message clipped]

You can remove any Remote* methods with [call_as()], and only include
the [local] method.

For typedefs, you basicly look up how the type is defined, and replace
it with the original type.

Mattias

===
Mattias Sjögren (VB MVP)
mattias @ mvps.org
http://www.msjogren.net/dotnet/
Reply to this message...
Vote that this is a GOOD answer...
 
Open source windows
The Law Society’s guidelines on e-mail management
 
    
Eduardo A. Morcillo [MVP-VB]
I made some classes that wraps the OLE strcuture storage files. If you want
I can send you a copy, but it is still under development and when finished I
will put it in my site.

--
Eduardo A. Morcillo (MS MVP VB)
http://www.domaindlx.com/e_morcillo

Reply to this message...
Vote that this is a GOOD answer...
 
 
    
HansH
Hello,

After you import the type library from dsofile.dll, you can use the
PropertyReader object in C#

This is an example:

DSOleFile.PropertyReader p = new DSOleFile.PropertyReaderClass();

DSOleFile.DocumentProperties dp =
p.GetDocumentProperties("e:\\testfile.xls");

textBox1.Text = dp.Title;

richTextBox1.Text = dp.Comments;

Be warned that you have to place this code in a Try - Catch block for
handling files that store this info in an other way. (NTFS)

I still have to find some code to handle this.

Kind regards,

Hans Hinnekint

Click here to reveal e-mail address

"Thomas Schmidt" <Click here to reveal e-mail address> wrote in message
news:a9oohu$57l$00$Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
Vote that this is a GOOD answer...
 
Google Docs… no.
Twitter Elite
 
    
Eliyahu \(Vyacheslav\) Biktagirov
Look Nicholas Paldino answer to your question at
microsoft.public.dotnet.languages.csharp and please stop
postings.
Reply to this message...
Vote that this is a GOOD answer...
 
Auto-following on Twitter
Ubuntu and XP on one “desktop”
 
    
ghost00x Dark
hi guys

i used yhis code above and i have many errors, is there newer one ,
and i want to use stgopenstorageEx , how i can define it with it's types in c#

i want a correct definition to all Istrage interfaces
thanks alot
Reply to this message...
Vote that this is a GOOD answer...
 
 
 
System.ArgumentException
System.Array
System.ComponentModel.Win32Exception
System.Diagnostics.StackTrace
System.EventArgs
System.Exception
System.IntPtr
System.Reflection.MethodBase
System.Runtime.InteropServices.CallingConvention
System.Runtime.InteropServices.CharSet
System.Runtime.InteropServices.ComInterfaceType
System.Runtime.InteropServices.LayoutKind
System.Runtime.InteropServices.UnmanagedType
System.Windows.Forms.Cursors
System.Windows.Forms.DialogResult
System.Windows.Forms.OpenFileDialog




Ad
BootFX
Reliable and powerful .NET application framework.
Recession Busting Bespoke Software
Get through the recession by investing in bespoke software to decrease costs and create commercial opportunities.
Other DN247 Network Sites
.NET 247
SQL Server Wins
Old Skool Developer
 
Copyright © AMX Software Ltd 2008-2009. Portions copyright © Matthew Baxter-Reynolds 2001-2009. All rights reserved.
Contact Us - Terms of Use - Privacy Policy - .NET 247 is a member of the DN247 Network - 4.0.30129.1734