Jump to content











Photo
- - - - -

Help needed with DevioNet.dll


  • Please log in to reply
16 replies to this topic

#1 Jan-Willem Dubbeldam

Jan-Willem Dubbeldam
  • Members
  • 8 posts
  •  
    Netherlands

Posted 10 December 2013 - 12:35 PM

Hello Olof, First of all I want to compliment you on a great piece of work. I’m trying to create my own devio implementation with the devionet.dll library. Eventually I want to create a proxy through http(s). I have been playing around with this code:

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            char driveLetter = ImDiskAPI.FindFreeDriveLetter();

            string mountingPoint = driveLetter.ToString() + @":";

 

            DevioIpProxy dip = new DevioIpProxy(@"D:\tmp\mydisk.vhd");

            DevioShmService dss = new DevioShmService(dip, true);

 

            dss.StartServiceThreadAndMountImDisk(ImDiskFlags.Auto, mountingPoint);

        }

    }

 

    class DevioIpProxy : DevioProviderManagedBase

    {

        private String VhdPath;

        private long VhdSize;

 

        public DevioIpProxy(String PathToVhd)

        {

            VhdPath = PathToVhd;

 

            FileInfo VhdInfo = new FileInfo(VhdPath);

           

            VhdSize = VhdInfo.Length;

        }

 

        public override bool CanWrite

        {

            get { throw new NotImplementedException(); }

        }

 

        public override long Length

        {

            get { return VhdSize; }

        }

 

        public override int Read(byte[] buffer, int bufferoffset, int count, long fileoffset)

        {

            throw new NotImplementedException();

        }

 

        public override uint SectorSize

        {

            get { return Convert.ToUInt32(0); }

        }

 

        public override int Write(byte[] buffer, int bufferoffset, int count, long fileoffset)

        {

            throw new NotImplementedException();

        }

    }

}

 

After creating a DevioShmService object I can see the Length and SectorSize methods in my DevioIpProxy class are being called. When calling StartServiceThreadAndMountImDisk no mounting point is being created and no Read en Write methods are being called. Could please help me with this.


Edited by Jan-Willem Dubbeldam, 10 December 2013 - 12:36 PM.


#2 Olof Lagerkvist

Olof Lagerkvist

    Gold Member

  • Developer
  • 1411 posts
  • Location:Borås, Sweden
  •  
    Sweden

Posted 10 December 2013 - 12:57 PM

(I moved this question to a new topic.)

 

I would guess that your application exits before the service thread even gets a chance to start doing anything. Or at least that could be one problem, there could be others. You can take a look at the DiscUtilsDevio.exe source code to see how this could be done in a console tool like this. Specifically, you need to wait for the service thread to exit before your application exits.
 
For example:
        static void Main(string[] args)
        {
            char driveLetter = ImDiskAPI.FindFreeDriveLetter();
            string mountingPoint = driveLetter.ToString() + @":";
 
            DevioIpProxy dip = new DevioIpProxy(@"D:\tmp\mydisk.vhd");
            DevioShmService dss = new DevioShmService(dip, true);
 
            dss.StartServiceThreadAndMountImDisk(ImDiskFlags.Auto, mountingPoint);

            dss.WaitForServiceThreadExit();
        }

 

If you do this in a GUI application, you would typically dismount and wait for service thread(s) to exit in response to some close event or similar.



#3 Jan-Willem Dubbeldam

Jan-Willem Dubbeldam
  • Members
  • 8 posts
  •  
    Netherlands

Posted 10 December 2013 - 02:49 PM

Hello Olof,

 

Thanks for spending time on my question. I will implement the WaitForServiceThreadExit call, but my application won't reach this point because it gets stuck on the StartServiceThreadAndMountImDisk call. Am I using the wrong ImDiskFlags?



#4 Olof Lagerkvist

Olof Lagerkvist

    Gold Member

  • Developer
  • 1411 posts
  • Location:Borås, Sweden
  •  
    Sweden

Posted 10 December 2013 - 03:20 PM

Okay, then it seems that something hands when the service is initializing. Could you add a trace listener at the beginning of your program and see what the last written trace message is?
 
Such as:
Trace.Listeners.Add(new ConsoleTraceListener())
 

This will cause trace messages to be written to the console window.



#5 Jan-Willem Dubbeldam

Jan-Willem Dubbeldam
  • Members
  • 8 posts
  •  
    Netherlands

Posted 10 December 2013 - 03:30 PM

Unfortunately it is in Dutch:

 

Creating objects for shared memory communication 'devio-555576243'.
Unhandled exception in service thread: System.UnauthorizedAccessException:

Toegang tot het pad is geweigerd (translated: access to path denied).
   bij System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   bij System.IO.MemoryMappedFiles.MemoryMappedFile.CreateCore(SafeFileHandle fi
leHandle, String mapName, HandleInheritability inheritability, MemoryMappedFileS
ecurity memoryMappedFileSecurity, MemoryMappedFileAccess access, MemoryMappedFil
eOptions options, Int64 capacity)
   bij System.IO.MemoryMappedFiles.MemoryMappedFile.CreateNew(String mapName, In
t64 capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, Me
moryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritabi
lity)
   bij LTR.IO.ImDisk.Devio.Server.Services.DevioShmService.RunService()



#6 Olof Lagerkvist

Olof Lagerkvist

    Gold Member

  • Developer
  • 1411 posts
  • Location:Borås, Sweden
  •  
    Sweden

Posted 10 December 2013 - 03:49 PM

It looks like your process doesn't have access to creating globally named memory mapping objects. That's a bit strange and interesting.

 

Does it work if you run your program with elevated privileges? (Preferably entire Visual Studio environment in elevated mode, to make things easier.)



#7 Jan-Willem Dubbeldam

Jan-Willem Dubbeldam
  • Members
  • 8 posts
  •  
    Netherlands

Posted 10 December 2013 - 04:58 PM

Running in elevated mode solved the problem. I am now getting Read request in my class derived from DevioProviderManagedBased. The tracing now reports:

 

Creating objects for shared memory communication 'devio-1077864656'.
Created shared memory object, 2101248 bytes.
Raising service ready event.
Waiting for client to connect.
Wait finished. Disposing file mapping object.
Client connected, waiting for request.
Largest requested read size is now: 512 bytes

 

Thanks again for making this library available and helping me out.



#8 Olof Lagerkvist

Olof Lagerkvist

    Gold Member

  • Developer
  • 1411 posts
  • Location:Borås, Sweden
  •  
    Sweden

Posted 10 December 2013 - 08:19 PM

Sounds great that you got the problem solved! Thanks!

:cheers:

#9 Jan-Willem Dubbeldam

Jan-Willem Dubbeldam
  • Members
  • 8 posts
  •  
    Netherlands

Posted 10 December 2013 - 08:40 PM

For anyone interested in creating their own communication layer with DevioNet.dll, hereby my sample project (thanks to Olof's tips, and pardon my novice C# knowledge):

 

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LTR.IO.ImDisk;
using LTR.IO.ImDisk.Devio.Server.Providers;
using LTR.IO.ImDisk.Devio.Server.Services;
using System.Diagnostics;
//http://discutils.codeplex.com/
using DiscUtils;
using DiscUtils.Vhd;
using DiscUtils.Partitions;
using DiscUtils.Ntfs;
using System.Security.AccessControl;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string VhdFile = @"D:\tmp\DummyVHD.vhd";

            Trace.Listeners.Add(new ConsoleTraceListener());

            char driveLetter = ImDiskAPI.FindFreeDriveLetter();
            string mountingPoint = driveLetter.ToString() + @":";

            DevioProxy dp = new DevioProxy(VhdFile);
            DevioShmService dss = new DevioShmService(dp, true);

            Console.WriteLine(mountingPoint);

            dss.StartServiceThreadAndMountImDisk(ImDiskFlags.DeviceTypeHD | ImDiskFlags.TypeProxy, mountingPoint);

            dss.WaitForServiceThreadExit();
 
            Console.WriteLine("End");
            Console.ReadLine();
        }
    }

    class DevioProxy : DevioProviderManagedBase
    {
        private String VhdPath;
        private byte[] VhdByteArray;
        private long VhdSize;

        public DevioProxy(String PathToVhd)
        {
            VhdPath = PathToVhd;

            CreateDummyVHD();

            FileInfo VhdInfo = new FileInfo(VhdPath);
           
            VhdSize = VhdInfo.Length;

            VhdByteArray = File.ReadAllBytes(VhdPath);
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Length
        {
            get { return VhdSize; }
        }

        public override int Read(byte[] buffer, int bufferoffset, int count, long fileoffset)
        {
            Array.Copy(VhdByteArray, fileoffset, buffer, bufferoffset, count);

            return count;
        }

        public override uint SectorSize
        {
            get { return Convert.ToUInt32(0); }
        }

        public override int Write(byte[] buffer, int bufferoffset, int count, long fileoffset)
        {
            Array.Copy(buffer, bufferoffset, VhdByteArray, fileoffset, count);

            return count;
        }

        private void CreateDummyVHD()
        {
            long diskSize = 10 * 1024 * 1024;
            using (Stream vhdStream = File.Create(VhdPath))
            {
                Disk disk = Disk.InitializeDynamic(vhdStream, DiscUtils.Ownership.None, diskSize);
                BiosPartitionTable bpt = BiosPartitionTable.Initialize(disk);

                using (NtfsFileSystem fs = NtfsFileSystem.Format(vhdStream, "MyVHD", Geometry.FromCapacity(diskSize), 0, Geometry.FromCapacity(diskSize).TotalSectors))
                {
                    RawSecurityDescriptor sd = new RawSecurityDescriptor("O:WDG:WDD:(A;OICIID;FA;;;WD)"); // Everyone full control
                    fs.SetSecurity("", sd);

                    fs.CreateDirectory("Test");

                    //Stream stream = fs.OpenFile(@"Test\vncviewer.exe", FileMode.Create);
                    //byte[] content = File.ReadAllBytes(@"D:\vncviewer.exe");

                    Stream stream = fs.OpenFile(@"Test\test.txt", FileMode.Create);
                    byte[] content = Encoding.ASCII.GetBytes("Hello world!");

                    if (stream.CanWrite)
                    {
                        stream.Write(content, 0, content.GetLength(0));
                        stream.Close();
                    }
                }
            }
        }
    }
}


  • Olof Lagerkvist likes this

#10 v77

v77

    Silver Member

  • Team Reboot
  • 563 posts
  •  
    France

Posted 11 December 2013 - 12:46 PM

It looks like your process doesn't have access to creating globally named memory mapping objects. That's a bit strange and interesting.

 

Does it work if you run your program with elevated privileges? (Preferably entire Visual Studio environment in elevated mode, to make things easier.)

 

I have the same issue with ProxyCrypt, when I prepare the shared memory protocol. CreateFileMapping fails without administrative privileges, because of the use of the global namespace. But I wonder what we can use to avoid that.



#11 Olof Lagerkvist

Olof Lagerkvist

    Gold Member

  • Developer
  • 1411 posts
  • Location:Borås, Sweden
  •  
    Sweden

Posted 11 December 2013 - 01:11 PM

In one way it makes sense that ImDisk only accepts the Global namespace and that way requires administrative privileges for a proxy service. It is after something that needs to act carefully and that easily could crash or hang the system if invalid data is returned, so it makes sense to require that it runs with administrative privileges. On the other hand, there is also TCP/IP communication available that has no such security mechanism whatsoever.



#12 Jan-Willem Dubbeldam

Jan-Willem Dubbeldam
  • Members
  • 8 posts
  •  
    Netherlands

Posted 11 December 2013 - 07:31 PM

Hello Olof,

 

I am now trying to use the TCP/IP variant:

 

IPAddress ip = IPAddress.Parse("127.0.0.1");

DevioTcpService dss = new DevioTcpService(ip, 9000, dp, true);

Console.WriteLine(mountingPoint);

dss.StartServiceThreadAndMountImDisk(ImDiskFlags.DeviceTypeHD | ImDiskFlags.ProxyTypeTCP, mountingPoint);

dss.WaitForServiceThreadExit();

 

Unfortunately I am getting the following exception:

 

Setting up listener at 127.0.0.1:9000
Raising service ready event.
Unhandled exception in service thread: System.Net.Sockets.SocketException (0x800
04005): Een blokkeerbewerking is onderbroken door een aanroep naar WSACancelBloc
kingCall
   bij System.Net.Sockets.Socket.Accept()
   bij System.Net.Sockets.TcpListener.AcceptSocket()
   bij LTR.IO.ImDisk.Devio.Server.Services.DevioTcpService.RunService()
Setting up listener at 127.0.0.1:9000
Raising service ready event.

 

Do you have a suggestion what could cause this exception?

 

 

 

 

 

 

 



#13 Olof Lagerkvist

Olof Lagerkvist

    Gold Member

  • Developer
  • 1411 posts
  • Location:Borås, Sweden
  •  
    Sweden

Posted 12 December 2013 - 09:12 AM

It looks strange that you see the "setting up listener..." message twice. The actual cause of the exception is that the listening socket is closed for some reason, probably when the service object is disposed. There could be some different explanations to why it gets disposed. There could for example be some problem with the ImDiskAPI.CreateDevice call. The CreateDevice call really is supposed to throw an exception in that case that you should have seen in your program, but it could be related to some problem there anyway.

 

I would suggest that you compare your code to DiscUtilsDevio and try to find out what you may do different that could cause your problem.



#14 Jan-Willem Dubbeldam

Jan-Willem Dubbeldam
  • Members
  • 8 posts
  •  
    Netherlands

Posted 12 December 2013 - 06:40 PM

Hello Olof,

 

Thanks for your reply. I tested the accepting socket by running DevioTcpService.RunService() method instead of the DevioServiceBase.StartServiceAndMountImDisk() call. I tested it with 'telnet localhost 9000' and it handled the socket request correctly.

 

I switched back to the MemoryMappeFile method and now force my application to restart in elevated mode by:

 

WindowsPrincipal pricipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());

bool hasAdministrativeRight = pricipal.IsInRole(WindowsBuiltInRole.Administrator);

if (!hasAdministrativeRight)

{

ProcessStartInfo startInfo = new ProcessStartInfo();

startInfo.UseShellExecute = true;

startInfo.WorkingDirectory = Environment.CurrentDirectory;

startInfo.FileName = Application.ExecutablePath;

startInfo.Verb = "runas";

try

{

Process p = Process.Start(startInfo);

}

catch (System.ComponentModel.Win32Exception ex)

{

return;

}

return;

}



#15 Olof Lagerkvist

Olof Lagerkvist

    Gold Member

  • Developer
  • 1411 posts
  • Location:Borås, Sweden
  •  
    Sweden

Posted 13 December 2013 - 12:01 AM

Yes, RunService works because the problem is not about the RunService part or the port listening itself. It is rather something that for some reason disposes the service object and thereby stopping the port listener.
 
But it is of course easier and somewhat more efficient to continue with shared memory communication when that is possible to use.
 
Normally, you mark an application manifest as requiring administrative privileges. There is strictly speaking no need for you to check whether current user is member of Administrators group first. Because this application will always require administrative privileges, it would in my opinion be better to specify that requirement directly in the application manifest. If the user is not member of a group with administrative privileges, the UAC prompt would then ask for user name and password for an administrator account.

Example of app.manifest in a project that requires administrative privileges in all cases:
https://github.com/A...ct/app.manifest
 
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
        <requestedPrivileges>
            <requestedExecutionLevel
              level="requireAdministrator"
              uiAccess="false"/>
        </requestedPrivileges>
    </security>
  </trustInfo>
</asmv1:assembly>


#16 Jan-Willem Dubbeldam

Jan-Willem Dubbeldam
  • Members
  • 8 posts
  •  
    Netherlands

Posted 23 December 2013 - 03:43 PM

Hello Olof,

 

Thanks for all the tips and a great driver. I have been able to create a prototype application which produces a server side custom harddrive which is download and mounted at client side.

 

Merry Christmas and a Happy New (coding) Year.


Edited by Jan-Willem Dubbeldam, 23 December 2013 - 03:44 PM.

  • Olof Lagerkvist likes this

#17 Olof Lagerkvist

Olof Lagerkvist

    Gold Member

  • Developer
  • 1411 posts
  • Location:Borås, Sweden
  •  
    Sweden

Posted 23 December 2013 - 04:01 PM

Sounds great!

and same to you, Merry Christmas and Happy New Year! ...of Vrolijk Kerstfeest en Gelukkig Nieuwjaar! ;)




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users