I'm trying to read a INI file then dump the values into the registry for a setup application to function.
The INI file looks like this
[Setup Components]
Sybase Adaptive Server Anywhere Client=Yes
Borland Database Engine=Yes
BDERoot=c:\Program Files\Borland\BDE
Program Shortcuts=Yes
ODBC Data Service Name=Yes
Local Client = Yes
Sybase Admin = Yes
Sybase Directory= C:\Program Files\SQL Anywhere 16
DBRoot=c:\Database
Word Link=Yes
Installation Root Folder=c:\program
FireDAC=DB=asa16;eng=ENGINE;dbn=DBNAME;links=TCPIP{Host=127.0.0.1;Port=2638}
[Program Shortcuts]
Desktop=Yes
Start Menu=Yes
Program Title=PROGRAM
EXE Pathname=c:\program.exe
Parameters=DBNAME
Starting Directory=c:\
Icon=icon.ICO
[ODBC Data Service Name]
Data Service Name=DBNAME
Database File Name=c:\database\database.db
Database Name=DBNAME
Server Name=ENGINE
Comm Links=TCPIP{}
PrefetchBuffer=6000
PrefetchRows=50
CommBufferSize=1460
Compress=no
[Service]
Service Name=SQLANYs_DBNAME
Display Name=SQL Anywhere - DBNAME
Service Group=SQLANYServer
Params=-n DBNAME -x TCPIP{} "C:\database\database.db" -n DBNAME
So I need all those items to be dumped into the registry easily.
I'm using Visual Studio 2015, I did attempt to use Ludvik Jerabek's INI reader (http://www.codeproject.com/Articles/21896/INI-Reader-Writer-Class-for-C-VB-NET-and-VBScript) but had no luck getting that to function!
The code i did use for the above was the following:
Dim ini = New IniFile()
ini.Load("setup.ini")
Dim readValue = ini.Sections("Service").Keys("Service Name")
MessageBox.Show(readValue.ToString)
When running this code i got the following error : "Conversion from string "Service" to type "Integer" is not valid. -Also this method means naming each and every key in the INI file which would be quite some task!
I then went down another method after reading some help questions on here and i used the following:
Private Declare Auto Function GetPrivateProfileString Lib "kernel32" (ByVal lpAppName As String,
ByVal lpKeyName As String,
ByVal lpDefault As String,
ByVal lpReturnedString As StringBuilder,
ByVal nSize As Integer,
ByVal lpFileName As String) As Integer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim sb As StringBuilder
sb = New StringBuilder(500)
Dim readVal = GetPrivateProfileString("Service", "Service Name", "", sb, sb.Capacity, "setup.ini")
MessageBox.Show(readVal.ToString)
End Sub
However this just returns "0"
Any help would be grateful to find a way to get this reading from the INI and dumping to the registry
Using the given IniFile class, this will get you to the config setting values:
Private Sub INIButtonTest_Click(sender As Object, e As EventArgs) Handles INIButtonTest.Click
Try
Dim iniFilePath As String = "H:\Dev\StackOverflow\StackOverflowTest\StackOverflowApp\bin\Debug\test.ini"
Dim myIniFile As New IniFile
myIniFile.Load(iniFilePath)
Dim myValue As String = getIniValue(myIniFile, "Service", "Service Name")
If Not String.IsNullOrEmpty(myValue) Then
MessageBox.Show(String.Format("Found value: [{0}]", myValue))
End If
Catch ex As Exception
MessageBox.Show(String.Concat("Something went wrong:", ex.Message))
End Try
End Sub
Private Function getIniValue(iniFileInstance As IniFile, sectionName As String, sectionKey As String) As String
Dim myValue As String = String.Empty
For Each sect As IniFile.IniSection In iniFileInstance.Sections
If sect.Name = sectionName Then
For Each key As IniFile.IniSection.IniKey In sect.Keys
If key.Name = sectionKey Then
myValue = key.GetValue
Exit For
End If
Next
Exit For
End If
Next
Return myValue
End Function
As an alternate, the code for the original approach is very close to correct, but GetPrivateProfileString doesn't actually exist in Kernel32.dll. You need to add a W to the name, which makes the corrected code:
' With these imports
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Text
' Note the W on the function name
Private Declare Auto Function GetPrivateProfileStringW Lib "kernel32" (ByVal lpAppName As String,
ByVal lpKeyName As String,
ByVal lpDefault As String,
ByVal lpReturnedString As StringBuilder,
ByVal nSize As Integer,
ByVal lpFileName As String) As Integer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim sb As New StringBuilder(500)
Dim result As Integer = GetPrivateProfileStringW("Service", "Service Name", "", sb, sb.Capacity, "setup.ini")
If result > 0 Then
MessageBox.Show(sb.ToString())
Else
Dim ex As New Win32Exception(Marshal.GetLastWin32Error())
MessageBox.Show(ex.Message)
End If
End Sub
If you prefer not to use the W, or you want to call your function something different, you can also use the DllImportAttribute and change the function declaration to:
<DllImport("Kernel32.dll", CharSet:=CharSet.Auto,
SetLastError:=True, EntryPoint:="GetPrivateProfileStringW")>
Private Shared Function GetPrivateProfileString(ByVal lpAppName As String,
ByVal lpKeyName As String,
ByVal lpDefault As String,
ByVal lpReturnedString As StringBuilder,
ByVal nSize As Integer,
ByVal lpFileName As String) As Integer
End Function
This is what I have used since VB 3.0 and it has worked with all versions of windows since 3.1. There have been a few modifications to make it fit into the conventions of the newer dev tools but essentially they are the same routines.
These must be in a public class or in the public area of a form:
Public Declare Function OSGetPrivateProfileString% Lib "kernel32" Alias "GetPrivateProfileStringA"(ByVal AppName As String, ByVal KeyName As String, ByVal keydefault As String, ByVal ReturnString As String, ByVal NumBytes As Integer, ByVal FileName As String)
Public Declare Function OSWritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA"(ByVal AppName As String, ByVal KeyName As String, ByVal keydefault As String, ByVal FileName As String) As Integer
Then, somewhere in your form's code area or if you have a "functions.vb" class (where the above would be declared) place these two:
Public Shared Function GetINIString(ByVal strItem As String, ByVal strDefault As String, ByVal strSection As String, ByVal filename As String) As String
Const BUFFERSIZE As Integer = 16768
Dim strTemp As New String(Chr(0), BUFFERSIZE)
Dim stringSize As Long = 0
stringSize = OSGetPrivateProfileString%(strSection, strItem, strDefault, strTemp, BUFFERSIZE, filename)
Return Strings.Left(strTemp, stringSize)
End Function
AND
Public Shared Sub PutINIString(strItem As String, strValue As String, strSection As String, iniFilename As String)
Dim i As Integer = 0
i = OSWritePrivateProfileString(strSection, strItem, strValue, iniFilename)
End Sub
This continues to work in VB 2010
Related
I made some research, but I can't find something really "interesting". I tried my best to find any kind of documentation or questions that are closest to my case as following:
How to find main window title name of application
how to get the window title of a process
How to get the Title Bar Text by its Process Id
getting the name of a process
How do I get list of Process Names running
Check to see if process is running
How To Get Process Owner ID
How to get the title/name of the last active window?
Get Process ID from Window Title
and also
Process.GetProcessesByName Method
The code I am using to open the process window
Private Async Function ParentMethod() As Task
Dim filePath As String = Await Task.Run(
Function()
Return Directory.EnumerateFiles(My.Settings.Cartellasalvataggio, titolo & ".mp3",
SearchOption.AllDirectories).FirstOrDefault()
End Function)
If Not String.IsNullOrEmpty(filePath) Then
LinkLabel1.Text = "File exist already"
LinkLabel1.Visible = True
PictureBox7.Visible = True
Else
MsgBox("it doesn't exist")
End If
End Function
and the helper class
Imports System.IO
Imports System.Runtime.InteropServices
Public Class NativeMethods
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Function SHOpenFolderAndSelectItems(
pidlFolder As IntPtr, cidl As UInteger,
<[In], MarshalAs(UnmanagedType.LPArray)> apidl As IntPtr(),
dwFlags As UInteger) As Integer
End Function
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Sub SHParseDisplayName(
<MarshalAs(UnmanagedType.LPWStr)> name As String,
bindingContext As IntPtr, <Out> ByRef pidl As IntPtr,
sfgaoIn As UInteger, <Out> ByRef psfgaoOut As UInteger)
End Sub
Public Shared Sub OpenFolderAndSelectFile(filePath As String)
Dim dirPath As String = Path.GetDirectoryName(filePath)
Dim fileName As String = Path.GetFileName(filePath)
OpenFolderAndSelectFile(dirPath, fileName)
End Sub
Public Shared Sub OpenFolderAndSelectFile(dirPath As String, fileName As String)
Dim nativeFolder As IntPtr
Dim psfgaoOut As UInteger
SHParseDisplayName(dirPath, IntPtr.Zero, nativeFolder, 0, psfgaoOut)
If nativeFolder = IntPtr.Zero Then
' Log error, can't find folder
Return
End If
Dim nativeFile As IntPtr
SHParseDisplayName(Path.Combine(dirPath, fileName),
IntPtr.Zero, nativeFile, 0, psfgaoOut)
Dim fileArray As IntPtr()
If nativeFile = IntPtr.Zero Then
' Open the folder without the file selected if we can't find the file
fileArray = New IntPtr(-1) {}
Else
fileArray = New IntPtr() {nativeFile}
End If
SHOpenFolderAndSelectItems(nativeFolder, CUInt(fileArray.Length), fileArray, 0)
Marshal.FreeCoTaskMem(nativeFolder)
If nativeFile <> IntPtr.Zero Then
Marshal.FreeCoTaskMem(nativeFile)
End If
End Sub
End Class
then calling it with
NativeMethods.OpenFolderAndSelectFile(filepath,filename & "extension"))
Since I am opening the process this way and NOT with Process class, almost all of them are not suitable to be considered for my case as many of them refer to notepad, while I think the explorer window title and ID changes for every file ( obviously), while "notepad" process, stay "notepad".
I also tried BringToFront, but this latter moves a control in front of other controls, but in this case Explorer is not a control, right?
The least I want to do is to
Get a list of active windows & their process names
as It will waste memory and time usage for no reason as I will need to "filter" process to find my process.
Hope we can find a solution to this, Thanks in advance.
Mattia
This is the solution to it using FindWindowW e SetWindowPos Api.
It is showing Explorer folder on top of top most form.
<DllImport("user32.dll", EntryPoint:="FindWindowW")>
Public Shared Function FindWindowW(<MarshalAs(UnmanagedType.LPTStr)> ByVal lpClassName As String, <MarshalAs(UnmanagedType.LPTStr)> ByVal lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll")>
Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As Boolean
End Function
Shared ReadOnly HWND_TOPMOST As IntPtr = New IntPtr(-1)
Const SWP_NOSIZE As UInt32 = &H1
Const SWP_NOMOVE As UInt32 = &H2
Const SWP_SHOWWINDOW As UInt32 = &H40
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim inptr = FindWindowW("CabinetWClass", Nothing)
SetWindowPos(inptr, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE Or SWP_SHOWWINDOW)
End Sub
I have the Problem that my script writes only the default value to the new Ini-File. It sould read the value from the BackupConfig. I do not see my error, yet. The output of the MessageBox should be the old URL but it prints google.
I hope you can help me, thank you.
My script looks like this
My.Computer.FileSystem.MoveFile(ConfigName, "backup.Config.ini", True)
Thread.Sleep(2000)
Dim Config As IniFile = New IniFile(ConfigName)
Dim BackupConfig As IniFile = New IniFile("backup.Config.ini")
MessageBox.Show(BackupConfig.GetString("Window08", "Url", "http://google.de"))
Config.WriteInteger("Setup", "Opacity", BackupConfig.GetInteger("Setup", "Opacity", 100))
This is my IniFile Class, i found it here on Stackoverflow an do not know which user posted it. (Sorry for that)
Public Class IniFile
' API functions
Private Declare Ansi Function GetPrivateProfileString _
Lib "kernel32.dll" Alias "GetPrivateProfileStringA" _
(ByVal lpApplicationName As String, _
ByVal lpKeyName As String, ByVal lpDefault As String, _
ByVal lpReturnedString As System.Text.StringBuilder, _
ByVal nSize As Integer, ByVal lpFileName As String) _
As Integer
Private Declare Ansi Function WritePrivateProfileString _
Lib "kernel32.dll" Alias "WritePrivateProfileStringA" _
(ByVal lpApplicationName As String, _
ByVal lpKeyName As String, ByVal lpString As String, _
ByVal lpFileName As String) As Integer
Private Declare Ansi Function GetPrivateProfileInt _
Lib "kernel32.dll" Alias "GetPrivateProfileIntA" _
(ByVal lpApplicationName As String, _
ByVal lpKeyName As String, ByVal nDefault As Integer, _
ByVal lpFileName As String) As Integer
Private Declare Ansi Function FlushPrivateProfileString _
Lib "kernel32.dll" Alias "WritePrivateProfileStringA" _
(ByVal lpApplicationName As Integer, _
ByVal lpKeyName As Integer, ByVal lpString As Integer, _
ByVal lpFileName As String) As Integer
Dim strFilename As String
' Constructor, accepting a filename
Public Sub New(ByVal Filename As String)
strFilename = Filename
End Sub
' Read-only filename property
ReadOnly Property FileName() As String
Get
Return strFilename
End Get
End Property
Public Function GetString(ByVal Section As String, _
ByVal Key As String, ByVal [Default] As String) As String
' Returns a string from your INI file
Dim intCharCount As Integer
Dim objResult As New System.Text.StringBuilder(256)
intCharCount = GetPrivateProfileString(Section, Key, _
[Default], objResult, objResult.Capacity, strFilename)
If intCharCount > 0 Then GetString = _
Left(objResult.ToString, intCharCount)
End Function
Public Function GetInteger(ByVal Section As String, _
ByVal Key As String, ByVal [Default] As Integer) As Integer
' Returns an integer from your INI file
Return GetPrivateProfileInt(Section, Key, _
[Default], strFilename)
End Function
Public Function GetBoolean(ByVal Section As String, _
ByVal Key As String, ByVal [Default] As Boolean) As Boolean
' Returns a boolean from your INI file
Return (GetPrivateProfileInt(Section, Key, _
CInt([Default]), strFilename) = 1)
End Function
Public Sub WriteString(ByVal Section As String, _
ByVal Key As String, ByVal Value As String)
' Writes a string to your INI file
WritePrivateProfileString(Section, Key, Value, strFilename)
Flush()
End Sub
Public Sub WriteInteger(ByVal Section As String, _
ByVal Key As String, ByVal Value As Integer)
' Writes an integer to your INI file
WriteString(Section, Key, CStr(Value))
Flush()
End Sub
Public Sub WriteBoolean(ByVal Section As String, _
ByVal Key As String, ByVal Value As Boolean)
' Writes a boolean to your INI file
WriteString(Section, Key, CStr(CInt(Value)))
Flush()
End Sub
Private Sub Flush()
' Stores all the cached changes to your INI file
FlushPrivateProfileString(0, 0, 0, strFilename)
End Sub
End Class
I am trying to write text to Win32 resources, but I have failed with it.
Here it is after writing the text:
And here is how it should look like:
Here's my code:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
WriteResourceStr(Target.Text, "hello")
End Sub
#Region "Second"
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
Private Shared Function UpdateResource(ByVal hUpdate As IntPtr, ByVal lpType As String, ByVal lpName As String, ByVal wLanguage As UShort, ByVal lpData As IntPtr, ByVal cbData As UInteger) As Boolean
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
Private Shared Function BeginUpdateResource(ByVal pFileName As String, <MarshalAs(UnmanagedType.Bool)> ByVal bDeleteExistingResources As Boolean) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
Private Shared Function EndUpdateResource(ByVal hUpdate As IntPtr, ByVal fDiscard As Boolean) As Boolean
End Function
Public Function WriteResourceStr(ByVal filename As String, ByVal bytes As String) As Boolean
Try
Dim handle As IntPtr = BeginUpdateResource(filename, False)
Dim file1 As String = bytes
Dim fileptr As IntPtr = ToPtr(file1)
Dim res As Boolean = UpdateResource(handle, "RCData", "CONFIG", 1, fileptr, System.Convert.ToUInt16(file1.Length))
EndUpdateResource(handle, False)
Catch ex As Exception
Return False
End Try
Return True
End Function
Private Function ToPtr(ByVal data As Object) As IntPtr
Dim h As GCHandle = GCHandle.Alloc(data, GCHandleType.Pinned)
Dim ptr As IntPtr
Try
ptr = h.AddrOfPinnedObject()
Finally
h.Free()
End Try
Return ptr
End Function
#End Region
So seems like it doesn't write ANSI, but in Unicode. How to change that?
Hopefully somebody replies.
The simplest way to get this is to just overload UpdateResource and let Windows make the Unicode to ANSI conversion for you:
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Ansi)> _
Private Shared Function UpdateResource(ByVal hUpdate As IntPtr, _
ByVal lpType As String, ByVal lpName As String, ByVal wLanguage As UShort,
ByVal lpData As String, ByVal cbData As Integer) As Boolean
End Function
Note the changed lpData type and the changed CharSet. The call now simply becomes:
Dim handle As IntPtr = BeginUpdateResource(filename, False)
If handle = IntPtr.Zero Then Throw New Win32Exception
Dim res As Boolean = UpdateResource(handle, "RCData", "CONFIG", 1, _
bytes, bytes.Length)
If Not EndUpdateResource(handle, False) Then Throw New Win32Exception
I'll have to restate the nonsensical nature of the call. RCData is a numbered resource type and not a string. Using a language ID of 1 makes little sense, that's Arabic so you wouldn't expect a Latin string in the resource. Whatever app reads this resource is unlikely to find it back.
Doing this correctly would require an overload that declares lpType as IntPtr so you can pass CType(10, IntPtr) for the RT_RCData resource type. The ToPtr() function is extremely evil, it returns a dangling pointer that will cause random data corruption. Just let the pinvoke marshaller generate the pointer by declaring the lpData argument as Byte(). You'd then use Encoding.GetBytes() to use the proper ANSI conversion. Thus:
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
Private Shared Function UpdateResource(ByVal hUpdate As IntPtr, _
ByVal lpType As IntPtr, ByVal lpName As String, ByVal wLanguage As UShort,
ByVal lpData As Byte(), ByVal cbData As Integer) As Boolean
End Function
With an additional overload required if lpName is a numbered instead of a named resource, use IntPtr.
When i am Running this Block of code it runs, however when i try to change the running process memory address's string value to somethinhg else it give me an error:
"System.IndexoutofRangeException Index was outside the Bounds of the Array"
These are the Functions:
<DllImport("kernel32.dll", SetLastError:=True)> _
Public Shared Function WriteProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As Byte(), ByVal nSize As System.UInt32, <Out()> ByRef lpNumberOfBytesWritten As Int32) As Boolean
End Function
Public Shared Function StrToByteArray(ByVal str As String) As Byte()
Dim encoding As New System.Text.ASCIIEncoding()
Return encoding.GetBytes(str)
End Function
Public Shared Function Poke(ByVal proc As Process, ByVal target As Integer, ByVal data As Byte()) As Boolean
Return WriteProcessMemory(proc.Handle, New IntPtr(target), data, data.Length, 0)
End Function
This is the button which executes the changed memory address value string.
Private Sub saveButton_Click(sender As Object, e As EventArgs) Handles saveButton.Click
Try
Dim p As Process() = Process.GetProcessesByName(AppName.Text)
Dim Written As Boolean = False
Written = Poke(p(0), &HB8FDCC, StrToByteArray(TxtVal.Text))
If Written = True Then
MsgBox("WriteProcessMemory Sucess!", MsgBoxStyle.OkOnly, "Poke Memory Status")
ElseIf Written = False Then
MsgBox("WriteProcessMemory Failed!", MsgBoxStyle.OkOnly, "Poke Memory Status")
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
End Class
Do not add the extension of program/application in the name of Process,
in your case, for Chrome
AppName.Text must be "Chrome" instead of "Chrome.exe",
Good Luck.
I ended up a simple program sending and receiving a RS-232 message. My parameters (baudrate, COM port) are stored in an INI file (the file will be created automatically if not existing). The progam runs without error but I don't know why it cuts off the path that point to INI file when the path length exceed a limit (there are Unicode Japanese characters in the path string)
My path string in New function (construction) is like this: "D:\通信プログラム20120709\新しいフォルダー\新しいフォルダー\新しいフォルダー\新しいフォルダー\Debug\Config.ini"
and in an event function, it will become: D:\通信プログラム20120709\新しいフォルダー\新しいフォルダー\新しいフォ・
After consulting some source on Internet, they show me that a .NET String would have a very huge capacity so I guess my problem not concerned to VB.NET String.
Any help would be appreciated.
From Comments
I found that the path is changed after the first line of code below
RS232TransPort = IniRoutine.GetString(IniSectionName, ConfigName.COMPort, "COM3")
RS232Baudrate = IniRoutine.GetInteger(IniSectionName, ConfigName.Baudrate, 9600)
This is the function to get a string:
Public Function GetString(ByVal Section As String, ByVal Key As String, ByVal [Default] As String) As String
Dim intCharCount As Integer
Dim objResult As New System.Text.StringBuilder(256)
intCharCount = GetPrivateProfileString(Section, Key, [Default], objResult, objResult.Capacity, strFilename)
GetString = String.Empty
If intCharCount > 0 Then GetString = Left(objResult.ToString, intCharCount)
End Function
where strFilename is a local variable of this class.
And this is the API declaire:
Private Declare Ansi Function GetPrivateProfileString _
Lib "kernel32.dll" Alias "GetPrivateProfileStringA" _
(ByVal lpApplicationName As String, _
ByVal lpKeyName As String, ByVal lpDefault As String, _
ByVal lpReturnedString As System.Text.StringBuilder, _
ByVal nSize As Integer, ByVal lpFileName As String) _
As Integer
You are using GetPrivateProfileStringA instead of GetPrivateProfileStringW.
Since you are using Unicode, you need to use GetPrivateProfileStringW instead.