all groups > dotnet interop > november 2005 >
You're in the

dotnet interop

group:

managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi



Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Mattias Sjögren
11/25/2005 12:00:00 AM
dotnet interop: [quoted text, click to view]

Where did unmanagedArray come from?


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
11/25/2005 7:37:53 AM
Hi, I've got a problem storing a string into unmanaged memory, witch
can't be restored to managed code.
I've build a NT Service manager, witch acces the Service properties
throug advapi32 calls.
I'm pretty sure the string is stored correct into the unmanged code,
because thru the default windows service manager I found the right
value,
only I want to get the string back myselft thru a call from c#.

Thnx!

some code parts:

//** The struct for setting the service failure actions **//
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_FAILURE_ACTIONS
{
[MarshalAs(UnmanagedType.U4)]
public int dwResetPeriod;
public IntPtr lpRebootMsg;
public IntPtr lpCommand;
[MarshalAs(UnmanagedType.U4)]
public int cActions;
[MarshalAs(UnmanagedType.U4)]
public int lpsaActions;
}


//** Setting the string to a unmanaged place**//

IntPtr tmpMsg = IntPtr.Zero;

tmpMsg = Marshal.StringToHGlobalAnsi("MY TEST STRING!!!");

// Set the SERVICE_FAILURE_ACTIONS struct
SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS();

sfa.cActions = numActions;
sfa.dwResetPeriod = this.failResetTime;
sfa.lpCommand = tmpCmd;
sfa.lpRebootMsg = tmpMsg;
sfa.lpsaActions = tmpBuf.ToInt32();

// Call the ChangeServiceFailureActions() abstraction of
ChangeServiceConfig2()
rslt = ChangeServiceFailureActions(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa);


//** Store the unmanaged string into a managed string **//
sfa = (SERVICE_FAILURE_ACTIONS)Marshal.PtrToStructure(unmanagedArray,
typeof(SERVICE_FAILURE_ACTIONS));
// cAtions, lpsaActions are correct in the sfa object.
string rebootMsg = Marshal.PtrToStringAnsi(sfa.lpRebootMsg);
// this string (rebootMsg) is incorrect
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
11/25/2005 8:49:17 AM
Sorry, not all code paste on previous message.
The code for getting the string from unmanaged code:

bool fRet = false;
uint dwBytesNeeded = 8192;
SERVICE_FAILURE_ACTIONS sfa;

IntPtr nullValue = new IntPtr(0);
fRet = QueryServiceConfiguration(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, nullValue, 0, ref dwBytesNeeded);

IntPtr unmanagedArray = Marshal.AllocHGlobal((int)dwBytesNeeded);
fRet = QueryServiceConfiguration(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, unmanagedArray, dwBytesNeeded, ref
dwBytesNeeded);

sfa = (SERVICE_FAILURE_ACTIONS)Marshal.PtrToStructure(unmanagedArray,
typeof(SERVICE_FAILURE_ACTIONS));

string reboot = Marshal.PtrToStringAnsi(sfa.lpRebootMsg);


/////////////////

//The QueryServiceConfiguration method is:

// Win32 function to Query on the service config config for the advanced
service properties
[DllImport("advapi32.dll", EntryPoint = "QueryServiceConfig2")]
public static extern bool
QueryServiceConfiguration(
IntPtr hService, int dwInfoLevel,
IntPtr lpInfo,
uint size, ref uint bytesneeded);

--
Sent via .NET Newsgroups
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Mattias Sjögren
11/25/2005 9:00:52 PM
Bram,

There appears to be a bug in QueryServiceConfig2A, the ANSI version of
the API. Like most ANSI APIs it calls the Unicode (W) version
internally and then converts the strings back to ANSI but in this case
it does so incorrectly. The workaround is to call the Unicode version
instead.

Just add CharSet=CharSet.Auto (or CharSet.Unicode) to your DllImport
attributes and use PtrToStringAuto or PtrToStringUnicode instead.


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
11/29/2005 7:50:24 AM
Mattias,
Thnx again for your reply. Unfortunatly the string can't be stored as
Unicode, because when i do that the 'lpCommand' LPTSTR from the
SERVICE_FAILURE_ACTIONS, that i've set isn't stored correct (as i can
see in the Service Recovery window, from Windows). Also adding the
CharSet attribute to the Dllimport results in a incorrect string.
As i store the string as
Marshal.StringToHGlobalAnsi(this.failRunCommand); as told in my
previous message, it is stored correctly (as i see in service
properties), but the code below gets a string thru the
'QueryServiceConfig2' Apicall that's incorrect. What I'm I doing wrong?

IntPtr scmHndl = IntPtr.Zero;
IntPtr svcHndl = IntPtr.Zero;
// Open the service control manager
scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);

// Open the service
svcHndl = OpenService(scmHndl, "Actemium Service_x",
SERVICE_ALL_ACCESS);

// Get the configuration information.
bool fRet = false;
uint dwBytesNeeded = 8192;
SERVICE_FAILURE_ACTIONS sfa;

IntPtr nullValue = new IntPtr(0);
fRet = QueryServiceConfiguration2(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, nullValue, 0, ref dwBytesNeeded);

IntPtr unmanagedArray = Marshal.AllocHGlobal((int)dwBytesNeeded);
fRet = QueryServiceConfiguration2(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, unmanagedArray, dwBytesNeeded, ref
dwBytesNeeded);

sfa = (SERVICE_FAILURE_ACTIONS)Marshal.PtrToStructure(unmanagedArray,
typeof(SERVICE_FAILURE_ACTIONS));

string reboot = Marshal.PtrToStringAnsi(sfa.lpRebootMsg);
//The svcHndl seems correct because the lpsaActions and the cActions
are correct
//Unfortunatly the sfa.lpRebootMsg IntPtr is incorrect and results in a
half or uncorrect string
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Mattias Sjögren
11/29/2005 11:01:00 PM

[quoted text, click to view]

Can you post your Unicode-ified code together with the DllImports and
struct? I think it should work if done right.


[quoted text, click to view]

If you're still calling the ANSI version of QueryServiceConfig2 then
it's not something you're doing wrong, it's the API bug.


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
12/2/2005 1:42:22 AM
I've put the code together with dllimports and struct into a test
project, hope you see what's wrong.

Bram.

[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_FAILURE_ACTIONS
{
[MarshalAs(UnmanagedType.U4)]
public int dwResetPeriod;
//[MarshalAs(UnmanagedType.LPTStr)]
//public string lpRebootMsg;
//[MarshalAs(UnmanagedType.LPTStr)]
//public string lpCommand;
[MarshalAs(UnmanagedType.U4)]
public int lpRebootMsg;
[MarshalAs(UnmanagedType.U4)]
public int lpCommand;
[MarshalAs(UnmanagedType.U4)]
public int cActions;
[MarshalAs(UnmanagedType.U4)]
public int lpsaActions;
}

[DllImport("advapi32.dll")]
public static extern IntPtr OpenSCManager(string lpMachineName, string
lpDatabaseName, int dwDesiredAccess);

[DllImport("advapi32.dll")]
public static extern IntPtr OpenService(IntPtr hSCManager, string
lpServiceName, int dwDesiredAccess);

[DllImport("advapi32.dll")]
public static extern IntPtr LockServiceDatabase(IntPtr hSCManager);

[DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2")]
public static extern bool ChangeServiceFailureActions(IntPtr hService,
int dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref
SERVICE_FAILURE_ACTIONS lpInfo);

[DllImport("advapi32.dll", EntryPoint = "QueryServiceConfig2A", CharSet
= CharSet.Ansi)]
public static extern bool QueryServiceConfiguration(IntPtr hService,
int dwInfoLevel, IntPtr lpInfo, uint size, ref uint bytesneeded);

[DllImport("advapi32.dll")]
public static extern bool CloseServiceHandle(IntPtr hSCObject);

[DllImport("advapi32.dll")]
public static extern bool UnlockServiceDatabase(IntPtr hSCManager);

private const int SC_MANAGER_ALL_ACCESS = 0xF003F;
private const int SERVICE_ALL_ACCESS = 0xF01FF;
private const int SERVICE_CONFIG_FAILURE_ACTIONS = 0x2;
private const int SERVICE_NO_CHANGE = -1;
private const int ERROR_ACCESS_DENIED = 5;

public ArrayList FailureActions;

private void SetFailureActions()
{ //Register the failure actions
FailureActions.Clear();
FailureActions.Add(new FailureAction(RecoverAction.Restart,
60000));
FailureActions.Add(new FailureAction(RecoverAction.RunCommand,
2000));
FailureActions.Add(new FailureAction(RecoverAction.None, 3000));

IntPtr scmHndl = IntPtr.Zero;
IntPtr svcHndl = IntPtr.Zero;
IntPtr tmpBuf = IntPtr.Zero;
IntPtr svcLock = IntPtr.Zero;
IntPtr tmpMsg = IntPtr.Zero;
IntPtr tmpCmd = IntPtr.Zero;

try
{
scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
svcLock = LockServiceDatabase(scmHndl);
svcHndl = OpenService(scmHndl, "MyServiceName",
SERVICE_ALL_ACCESS);

int numActions = FailureActions.Count;
int[] actions = new int[numActions * 2];
int currInd = 0;
foreach (FailureAction fa in FailureActions)
{
actions[currInd] = (int)fa.Type;
actions[++currInd] = fa.Delay;
currInd++;
//If the FailureActions is reboot then Grand shutdown
privilege
}

tmpBuf = Marshal.AllocHGlobal(numActions * 8);
Marshal.Copy(actions, 0, tmpBuf, numActions * 2);

tmpCmd = Marshal.StringToHGlobalAnsi("MyCommand.exe");
tmpMsg = Marshal.StringToHGlobalAnsi("Time to reboot!");
//tmpMsg = Marshal.StringToHGlobalUni("Time to reboot!");

SERVICE_FAILURE_ACTIONS sfa = new SERVICE_FAILURE_ACTIONS();

sfa.cActions = numActions;
sfa.dwResetPeriod = SERVICE_NO_CHANGE;
//sfa.lpCommand = "My Test Command";
//sfa.lpRebootMsg = "My Reboot Message";
sfa.lpCommand = tmpCmd.ToInt32();
sfa.lpRebootMsg = tmpMsg.ToInt32();
sfa.lpsaActions = tmpBuf.ToInt32();

bool rslt = ChangeServiceFailureActions(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa);
Marshal.FreeHGlobal(tmpBuf); tmpBuf = IntPtr.Zero;
}
catch (Exception ex)
{ Console.WriteLine(ex.Message); }
finally
{
if (scmHndl != IntPtr.Zero)
{ if (svcLock != IntPtr.Zero)
{ UnlockServiceDatabase(svcLock);
svcLock = IntPtr.Zero; }
CloseServiceHandle(scmHndl);
scmHndl = IntPtr.Zero; }
if (svcHndl != IntPtr.Zero)
{ CloseServiceHandle(svcHndl);
svcHndl = IntPtr.Zero; }
if (tmpBuf != IntPtr.Zero)
{ Marshal.FreeHGlobal(tmpBuf);
tmpBuf = IntPtr.Zero; }
}
}

private void GetFailureActions()
{
IntPtr scmHndl = IntPtr.Zero;
IntPtr svcHndl = IntPtr.Zero;
IntPtr tmpBuf = IntPtr.Zero;
IntPtr nullValue = new IntPtr(0);

try
{
scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
svcHndl = OpenService(scmHndl, "MyServiceName",
SERVICE_ALL_ACCESS);

uint dwBytesNeeded = 8192;
SERVICE_FAILURE_ACTIONS sfa;

bool fRet = QueryServiceConfiguration(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, nullValue, 0, ref dwBytesNeeded);

IntPtr unmanagedArray =
Marshal.AllocHGlobal((int)dwBytesNeeded);
fRet = QueryServiceConfiguration(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, unmanagedArray, dwBytesNeeded, ref
dwBytesNeeded);

sfa =
(SERVICE_FAILURE_ACTIONS)Marshal.PtrToStructure(unmanagedArray,
typeof(SERVICE_FAILURE_ACTIONS));

//Why are the sfa.lpRebootMsg and lpCommand Pointers below,
incorrect??
string reboot = Marshal.PtrToStringAnsi(new
IntPtr(sfa.lpRebootMsg));
string command = Marshal.PtrToStringAnsi(new
IntPtr(sfa.lpCommand));
//string command = Marshal.PtrToStringUni(new
IntPtr(sfa.lpCommand));

Int32[] bins = new Int32[sfa.cActions * 2];
int offset = sfa.lpsaActions;

for (int i = 0; i < sfa.cActions * 2; i++)
{ bins[i]=((Int32)Marshal.ReadIntPtr(new IntPtr(offset + i *
4))); }
Marshal.Release(unmanagedArray);
}
catch (Exception ex)
{ Console.WriteLine(ex.Message); }
}


public enum RecoverAction{ None=0, Restart=1, Reboot=2, RunCommand=3 }

public class FailureAction
{
private RecoverAction type = RecoverAction.None;
private int delay=0;
public FailureAction(){}
public FailureAction( RecoverAction actionType, int actionDelay ){
this.type = actionType;
this.delay = actionDelay;
}

public RecoverAction Type{ get{ return type; } set{ type = value;}
}
public int Delay{ get{ return delay; } set{ delay = value;} }
}
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
12/8/2005 4:41:38 AM
Anyone any idea why the string reboot and command, in my previous post
aren't 'Time to reboot!' and 'MyCommand.exe' ?
I'm still struggling with this code, and can't find any more resources.
Do you know where to look for more information about the use of the
'QueryServiceConfig2A' Api call, or an example with the
SERVICE_FAILURE_ACTIONS ?
thanx,
bram
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Willy Denoyette [MVP]
12/8/2005 9:46:50 PM

[quoted text, click to view]
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
12/16/2005 3:10:19 AM

Willy Denoyette [MVP] schreef:
[quoted text, click to view]

Thnx for your response Willy, unfortunatly your code results the same
reboot and command strings as mine. mostly: "???\v" etc.

bram
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Willy Denoyette [MVP]
12/17/2005 5:08:52 PM

[quoted text, click to view]

This sample works for me. Did you try to build and run the code snip I
posted?

Willy.

Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
12/17/2005 5:13:47 PM

[quoted text, click to view]

Yes i'v tried your console sample on 2 pc's running win2k. With my own
service and the winmngr serive, all result in a string containing
unknown characters.
bram
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Willy Denoyette [MVP]
12/19/2005 12:20:50 AM

[quoted text, click to view]

Change the signatures of the functions such that the unicode version is
used.


[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError =
true)]
public static extern bool QueryServiceConfig2(IntPtr hService,
int dwInfoLevel, IntPtr lpInfo, uint size, ref uint bytesneeded);

or:

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError = true)]


Willy.

Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
12/19/2005 2:52:13 AM
I've changed it to Unicode (as Mattias Sj=F6gren suggested earlier), but
still the same results.
Also tried it on a win XP and 2003 virtual PC.
I'm close to give up query the Service Failure Actions...
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Bram Hoefnagel
12/19/2005 7:40:21 AM
Yeah! that's it!
The CharSet of the struct must be set.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SERVICE_FAILURE_ACTIONS

Thnx for helping me finding a solution!

--
Sent via .NET Newsgroups
Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Willy Denoyette [MVP]
12/19/2005 4:09:38 PM

[quoted text, click to view]
I've changed it to Unicode (as Mattias Sjögren suggested earlier), but
still the same results.
Also tried it on a win XP and 2003 virtual PC.
I'm close to give up query the Service Failure Actions...


Well, I don't get it, it works for me on XP and W2K3.
This is the output of the program[1], after I added "RebootWinmgmt" and
"CommandWinmgmt" to the winmgmt service using:
sc qfailure reboot= RebootWinmgmt
sc qfailure command= CommandWinmgmt

output of [1]
Reset Period: 86400
Reboot: RebootWinmgmt
Command: CommandWinmgmt
Actions: 2
1
60000
1
60000

[1]
using System;
using System.Runtime.InteropServices;
using System.Collections;
using System.Text;
namespace Willys
{
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct SERVICE_FAILURE_ACTIONS
{
public int dwResetPeriod;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpRebootMsg;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpCommand;
public int cActions;
public IntPtr lpsaActions;
}

class Tester
{

[DllImport("advapi32.dll")]
public static extern IntPtr OpenSCManager(string lpMachineName, string
lpDatabaseName, int dwDesiredAccess);

[DllImport("advapi32.dll")]
public static extern IntPtr OpenService(IntPtr hSCManager, string
lpServiceName, int dwDesiredAccess);

[DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError =
true)]
public static extern bool QueryServiceConfig2(IntPtr hService,
int dwInfoLevel, IntPtr lpInfo, uint size, ref uint bytesneeded);
private const int SC_MANAGER_ALL_ACCESS = 0xF003F;
private const int SERVICE_ALL_ACCESS = 0xF01FF;
private const int SERVICE_CONFIG_FAILURE_ACTIONS = 0x2;
private const int SERVICE_NO_CHANGE = -1;
private const int ERROR_ACCESS_DENIED = 5;

static void Main()
{
GetFailureActions();
}
static void GetFailureActions()
{
IntPtr scmHndl = IntPtr.Zero;
IntPtr svcHndl = IntPtr.Zero;
IntPtr tmpBuf = IntPtr.Zero;
IntPtr nullValue = new IntPtr(0);
IntPtr unmanagedArray = IntPtr.Zero;
try
{
scmHndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
svcHndl = OpenService(scmHndl, "winmgmt", SERVICE_ALL_ACCESS);

uint dwBytesNeeded = 0;
SERVICE_FAILURE_ACTIONS sfa;

bool fRet = QueryServiceConfig2(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, nullValue, 0, ref
dwBytesNeeded);
unmanagedArray = Marshal.AllocHGlobal((int)dwBytesNeeded);

fRet = QueryServiceConfig2(svcHndl,
SERVICE_CONFIG_FAILURE_ACTIONS, unmanagedArray, dwBytesNeeded,
ref dwBytesNeeded);
sfa =
(SERVICE_FAILURE_ACTIONS)Marshal.PtrToStructure(unmanagedArray,
typeof(SERVICE_FAILURE_ACTIONS));

Console.WriteLine("Reset Period: {0}",sfa.dwResetPeriod);
string reboot = sfa.lpRebootMsg;
string command = sfa.lpCommand;

if(reboot != null)
Console.WriteLine("Reboot: {0}",reboot);
if(command != null)
Console.WriteLine("Command: {0}",command);
Console.WriteLine("Actions: {0}",sfa.cActions);
int SIZE_OF_SC_ACTION = 2; // hardcode struct size in DWORD size
Int32[] bins = new Int32[sfa.cActions * SIZE_OF_SC_ACTION];
IntPtr offset = sfa.lpsaActions;

for (int i = 0; i < sfa.cActions * SIZE_OF_SC_ACTION; i++)
{
bins[i]=Marshal.ReadInt32(offset, i * sizeof(int));
}
foreach(int c in bins)
{
Console.WriteLine(c);
}
Marshal.Release(unmanagedArray);
}
catch (Exception ex)
{ Console.WriteLine(ex.Message); }
}

}
}

Willy.

Re: managed / unmanged - StringToHGlobalAnsi / PtrToStringAnsi Willy Denoyette [MVP]
12/19/2005 5:23:02 PM

[quoted text, click to view]

Sorry if I didn't explicitly stated this.
If you are using a Unicode version of a function, you should also use the
unicode version of it's arguments.
Anyway, the code should also work with the Ansi version, but here is the
problem (as you noticed). The ansi version returns a string that is partly
correct, the first part however is not correctly filled (that's why you
noticed the unprintable chars), this looks like a bug in the ansi version of
the function (I filed a bug). Note that these functions are only available
on NT like OS, so there should be no reason to use the ansi versions
whatsoever.

Willy.

AddThis Social Bookmark Button