čtení .ini   zodpovězená otázka

VB.NET

Zdravim,

když vložím do modulu tuto funkci pro čtení dat z .ini souborů:

Public Declare Function WritePrivateProfileString& Lib "KERNEL32" Alias "WritePrivateProfileStringA" (ByVal AppName$, ByVal KeyName$, ByVal keydefault$, ByVal FileName$)
    Public Declare Function GetPrivateProfileString& Lib "KERNEL32" Alias "GetPrivateProfileStringA" (ByVal AppName$, ByVal KeyName$, ByVal keydefault$, ByVal ReturnedString$, ByVal RSSize&, ByVal FileName$)

    Public Sub WriteINI(ByVal INISection As String, ByVal INIKey As String, ByVal INIValue As String, ByVal INIFile As String)
        Call WritePrivateProfileString(INISection, INIKey, INIValue, INIFile)
    End Sub

    Public Function ReadINI(ByVal INISection As String, ByVal INIKey As String, ByVal INIFile As String) As String
        Dim StringBuffer As String
        Dim StringBufferSize As Long
        StringBuffer = Space$(255)
        StringBufferSize = Len(StringBuffer)
        StringBufferSize = GetPrivateProfileString(INISection, INIKey, "", StringBuffer, StringBufferSize, INIFile)
        If StringBufferSize > 0 Then
            ReadINI = Left$(StringBuffer, StringBufferSize)
        Else
            ReadINI = ""
        End If
    End Function

tak při pokusu o spuštění programu mi to zahlásí chybu:

A call to PInvoke function 'server!modulDefault::GetPrivateProfileString' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

hlasí to pouze, když využiju kód z:

http://vbnet.cz/clanek--27-kreslici_tabu...

můžete mi poradit, jak tuto chybu odstranit?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý den,

a voláte to z hlavního aplikačního vlánka?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

no já jsem právě ty vlákna moc nepochopil, mohl by jste mi to objasnit? děkuji

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pro čtení se používá nové vlákno. Pokud to voláte z toho nového, je pravděpodobné, že nastala tato chyba.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

aha, a můžete mi napsat menší ukázku kódu?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Prosím Vás, abyste pro vkládání bloků kódu použil příslušné tlačítko VB code, aby se kód v příspěvku zvýraznil. Jinak je to nepřehledné.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Nebude to tím, že u těch API funkcí nemáte deklarovanou návratovou hodnotu a tudíž neodpovídá signatura?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

to nepomáhá...bude problém v těch vláknech, proto prosím o malou ukázku, jak bych to mohl opravit

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ve vláknech problém nebude. V popisu vyjímky máte jasně napsáno, že neodpovídá signatura metody a vy nemáte u těch funkcí uvedenou žádnou návratovou hodnotu (API funkce vrací hodnotu DWORD). Zkuste napsat na konec deklarace "As Long".

nahlásit spamnahlásit spam 0 odpovědětodpovědět

nepomohlo :(

nahlásit spamnahlásit spam 0 odpovědětodpovědět

A můžete sem napsat upravený zdrojový kód?

nahlásit spamnahlásit spam 0 odpovědětodpovědět
Public Declare Function GetPrivateProfileString Lib "KERNEL32" Alias "GetPrivateProfileStringA" (ByVal AppName$, ByVal KeyName$, ByVal keydefault$, ByVal ReturnedString$, ByVal RSSize&, ByVal FileName$) As Long

    Public Sub WriteINI(ByVal INISection As String, ByVal INIKey As String, ByVal INIValue As String, ByVal INIFile As String)
        Call WritePrivateProfileString(INISection, INIKey, INIValue, INIFile)
    End Sub
...
nahlásit spamnahlásit spam 0 odpovědětodpovědět

A když to uděláte v novém projektu, tak to funguje? Nebo to nejde nikde? Podle prvního příspěvku to vyznělo tak, že je problém jen při použití kódu v kreslící tabuli.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

ano vyznělo to správně, právě proto jsem to tam psal, tak si myslím, že bude problém v těch vláknech, jak jste psal...jenže já nevím jak to upravit, proto prosím o Vaši pomoc, ukázkou kódu

nahlásit spamnahlásit spam 0 odpovědětodpovědět

tak se omlouvám, chyba nastane i bez kódu kreslící tabule, předtím jsem to nejspíše špatně zkoušel...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ono je to totiž hlavně tím, že ta deklarace je špatně. Víceméně ve všech API funkcích ve VB6 se používal datový typ Long, protože ten ve VB6 odpovídal 32-bit integeru. VB.NET má ovšem Long 64-bitový, tudíž je nutné místo As Long napsat As Integer. Navíc specifikovat datové typy parametrů pomocí suffixů, to jsem již hezky dlouho neviděl. Raději bych viděl explicitní zápis, s těmi & a $ na konci je to nepřehledné.

Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Object, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer 

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

děkuji Tomáši,

chybu už to nepíše starou, ale pro změnu to začlo psát:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Můžete sem hodit kód, s jakým tu funkci voláte? Co tam předáváte za parametry?

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

V definici funkce GetPrivateProfileString změňte ... lpKeyName As Object na lpKeyName As String...

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

modul:

Module modulDefault
    Public ConfigFilePath As String = "\config.ini"

    Public Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal AppName As String, ByVal KeyName As String, ByVal keydefault As String, ByVal FileName As String)
    Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal AppName As String, ByVal KeyName As String, ByVal keydefault As String, ByVal ReturnedString As String, ByVal RSSize As Integer, ByVal FileName As String) As Integer

    Public Sub WriteINI(ByVal INISection As String, ByVal INIKey As String, ByVal INIValue As String, ByVal INIFile As String)
        Call WritePrivateProfileString(INISection, INIKey, INIValue, INIFile)
    End Sub

    Public Function ReadINI(ByVal INISection As String, ByVal INIKey As String, ByVal INIFile As String) As String
        Dim StringBuffer As String
        Dim StringBufferSize As Long
        StringBuffer = Space$(255)
        StringBufferSize = Len(StringBuffer)
        StringBufferSize = GetPrivateProfileString(INISection, INIKey, "", StringBuffer, StringBufferSize, INIFile)
        If StringBufferSize > 0 Then
            ReadINI = Left$(StringBuffer, StringBufferSize)
        Else
            ReadINI = ""
        End If
    End Function
End Module

formulář, ze kterého to volám:

Public Class formLoad
    Private Sub Me_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Text = frmTitle

        Dim tcpListener As Net.Sockets.TcpListener 'dokáže poslouchat příchozí TCP připojení 
        Dim Port As Integer = ReadINI("SERVER", "port", ConfigFilePath) 'budeme používat tento port 
        tcpListener = New Net.Sockets.TcpListener(System.Net.IPAddress.Any, Port)  'vytvoříme TCP Listener
...
    End Sub
End Class

teď to hlásí špatnou konverzi na řádku, kde deklaruji proměnnou Port: Conversion from string "" to type 'Integer' is not valid.

u proměnné port jsem zkoušel měnil typ i na ostatní, ale nejde, podle mne když to bude číslo, tak to má být Integer ne?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

V INI souboru máte např.:

[SERVER]

Port=1000

Funkce ReadINI čte a vrací hodnotu klíče Port z ini soboru jako String, ne jako číslo. INI soubor je textový soubor.

String musíte na Integer převést pomocí CInt

Dim Port As Integer = CInt(ReadINI("SERVER", "port", ConfigFilePath)) 

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

tak v tom případě by to mělo být:

Dim Port As String = CInt(ReadINI("SERVER", "port", ConfigFilePath)) 

ne?

zkoušel jsem oboje, a píše to stejnou hlášku:

Conversion from string "" to type 'Integer' is not valid.

už jsem zkoušel skoro vše, zdá se mi to jako snadná věc, a já si s ní stále takhle lámu hlavu :(

nahlásit spamnahlásit spam 0 odpovědětodpovědět

"tak v tom případě by to mělo být:" - jde právě o ten který případ. Nic nebrání, abyste hodnotu klíče definoval jako String. To vyhoví pro volání funkce k načtení hodnoty z INI souboru. Další je závislé na tom, k čemu přečtenou hodnotu použijete. Ve vašem případě jde o číslo portu. Potom pro jeho použití v

tcpListener = New Net.Sockets.TcpListener(System.Net.IPAddress.Any, Port)

musíte stejně konverzi provést. Proč to neudělat hned?

Váš problém by mohl být v tom, že si neuvědomujete, že parametr, který předáváte, musí mít takový typ, jaký funkce či metoda očekává. ReadINI očekává String a Net.Sockets.TcpListener očekává Integer. Tomu musíte svůj kód podřídit.

Dodatek - upřesnění:

Co jsem napsal o parametru je obecně platné, ve vašem případu jde o návratovou hodnotu. V tom platí totéž, co o parametru. Je-li návratová hodnota funkce ReadINI String, nelze ji definovat jako Integer. Vrací-li funkce String a vy potřebujete Integer, musíte provést konverzi.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

děkuji za vysvětlení, už jsem to konečně rozlouskl, problém byl v cestě k souboru config.ini

když tam zadám cestu v tomto tvaru:C:\Documents and Settings\...

tak to jede ok, ale já bych potřeboval něco jako:/config.ini

prostě že "/" bude jako by kořen projektu

takže kdyby soubor config.ini byl ve složce config

tak by cesta vypadala:/config/config.ini

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Výchozí místo je místo, kde je spouštěcí exe soubor. Odtud můžete adresovat další umístění. Do nižší úrovně začínáte adresu bez lomítka, tedy pokud složku config dáte tam, kde je exe soubor, adresujete jen: "config\config.ini". Chcete-li umístit složku config nad místo s exe souborem, pak adresujete "..\config\config.ini", o dvě úrovně výš "..\..\config\config.ini". To je tzv. relativní adresování, na rozdíl od adresování absolutního, tj. vypsáním úplné cesty (C:\Documents and Settings\...). Začnete-li adresu s "\" adresujete od kořene disku. Adresa "\config\config.ini" by při práci na disku C: znamenala "C:\config\config.ini"

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

děkuji za vyčerpávájící odpověď, opravdu jste mi pomohl.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

ještě maličkost, když v položce Compile nastavím Build output path na: .\

tak se projekt zkompiluje, tak kde se nachází projektový soubor, ale podle tohoto exe souboru se neřídí cesty k souborům tudíž nemohu použít:

Public ConfigFilePath As String = "config.ini"

nýbrž z exe souboru nacházejícího se ve složce obj/Release

tudíž musím použít:

Public ConfigFilePath As String = "..\..\config.ini"

jak mohu nastavit, aby mohl pro exe soubor v kořenu projektu používat normální cesty k souborům, jak jsem uvedl výše...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Nevím přesně, kde je chyba. Myslím si, že změnou místa pro kompilaci děláte v souborech zmatek. Nikdy mi nenapadlo defaultní strukturu měnit a kdykoliv jsem pak kompilaci kamkoliv překopíroval, tak přístup k souborům fungoval, jak jsem popsal. Je také fakt, že jsem do kompilace nemíchal jiné soubory, vždy jsem použil jinou úroveň.

Tak mi napadá jen jedna věc: sdělit, že config.ini se nachází tam co program, stejně jako jste to nastavil kompilátoru.

Zkuste tedy použít:

Public ConfigFilePath As String = ".\config.ini"

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

to je ono! opravdu děkuji, můžete nastavit status tématu jako vyřešeno.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Stačí v podokně Solution Explorer vybrat soubor congif.ini a jeho vlastnost Copy To Output Directory nastavit na hodnotu Copy If Newer. Tím se soubor automaticky při kompilaci hodí do složky, kde je i EXE soubor, pokud tam ještě není.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

také děkuji za další tip

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Problém asi nebyl v neexistenci souboru na stejném místě s EXE, ale adresování souboru. Setkávám se s tím poprvé. Nevím, proč jen jméno souboru nestačilo. Je snad vždy nutné adresovat soubory ve stejné složce s EXE s ".\" před jménem, nebo problém způsobila volba místa kompilace do hlavní složky projektu (dle mého nevhodná)?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

na Vaši radu jsem místo kompilace změnil zpět na původní, pro větší přehlednost.

".\" funguje jak má, tento zápis mne, abych se přiznal, také nenapadl, jelikož například v jazyce php zápis ".\" znamená, že se soubor nachází na tentýž úrovni, z kterého souboru to volám, pokud mne tedy neklame pamět :-), ale u VB.net to nejspíš znamená "KOŘEN" projektu

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ne, ne - význam jedné tečky je stále stejný, je to označení aktuálního adresáře (název z DOSu, dnes složky), dvě tečky jsou vyšší úrověň. Kořen, tj. nejvyšší úroveň adresářové struktury, je samotné lomítko. Rozdíl proti PHP je jen ve sklonu lomítka. Název "kořen" pro domovskou složku projektu je matoucí.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

aha dobře

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ještě by mohlo jít o případ, kdy konverze není možná, protože hodnota, kterou funkce vrátila, převést nelze, např, "", tj. prázdný řetězec. Ten máte nastavený jako defaultní návratovou hodnotu.

 StringBufferSize = GetPrivateProfileString(INISection, INIKey, "", _ 
     StringBuffer, StringBufferSize, INIFile)

Zkuste to změnit třeba na

        StringBufferSize = GetPrivateProfileString(INISection, INIKey, "???", StringBuffer, StringBufferSize, INIFile)
        If StringBufferSize > 0 Then
            ReadINI = Left$(StringBuffer, StringBufferSize)
        Else
            ReadINI = "nulová délka..."
        End If

Pracujte se String bez konverze a zkontrolujte, co funkce vrací a porovnejte s obsahem INI souboru. Konverzi použijte až dokážete, že hodnota klíče Port jsou potřebné číslice

... a " ReadINI = Left$(StringBuffer, StringBufferSize)" bych také změnil:

    If StringBufferSize > 0 Then
      ReadINI = StringBuffer.Substring(0, StringBufferSize)
    Else
      ReadINI = "nulová délka!"
    End If

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

prosím o uzavření tohoto tématu...

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback