OpenFileDialog發生問題

steren55 2013-07-12 10:08:42
小弟完全不知道發生了什麼事,還請各位大大們幫忙!
小弟宣告了一個struct如下:
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DataType
{
public float Parameter;
public float Setting;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public float[] Temp;
}
private static DataType MyData;
再宣告一個float[]如下:
private static float[] MyDataFloat = new float[4];
程式寫成按一個Button會開一個OpenFileDialog讓使用者選一個TXT檔,
程式會將TXT檔中的參數寫進float[],
再用以下的程式碼轉成struct,
IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(MyData) / 4);
Marshal.Copy(MyDataFloat, 0, buffer, Marshal.SizeOf(MyData) / 4);
MyData = (DataType)Marshal.PtrToStructure(buffer, MyData.GetType());
Marshal.FreeHGlobal(buffer);
整個動作就只有這樣,
奇怪的問題發生了,
按鍵按第一次沒事,
第二次偶爾沒事,
第三次總是會出現
"嘗試讀取或寫入受保護的記憶體。這通常表示其他記憶體已損毀。"
並且停在這行:
if (opendlg.ShowDialog() != DialogResult.OK) { return; }
小弟完全不知道為什麼?

以下貼出整個Button內的程式,請各位高手幫幫忙,怎麼寫才沒問題?
private void button5_Click(object sender, EventArgs e)
{
string FilePathName,str;
OpenFileDialog opendlg = new OpenFileDialog();
opendlg.Filter = "TXT檔(*.txt)|*.txt|所有檔案(*.*)|*.*";
if (opendlg.ShowDialog() != DialogResult.OK) { return; }

// Read TXT File
StreamReader file = new StreamReader(FilePathName);
str=file.ReadLine();
file.Close();

// 假裝已經拿到TXT的資料了
MyDataFloat[0] = 0.111F;
MyDataFloat[1] = 0.222F;
MyDataFloat[2] = 0.333F;
MyDataFloat[3] = 0.444F;

IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(MyData) / 4);
Marshal.Copy(MyDataFloat, 0, buffer, Marshal.SizeOf(MyData) / 4);
MyData = (DataType)Marshal.PtrToStructure(buffer, MyData.GetType());
Marshal.FreeHGlobal(buffer);
}

P.S.
IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(MyData) / 4);
Marshal.Copy(MyDataFloat, 0, buffer, Marshal.SizeOf(MyData) / 4);
MyData = (DataType)Marshal.PtrToStructure(buffer, MyData.GetType());
Marshal.FreeHGlobal(buffer);
以上程式刪掉就沒問題,
可是小弟單獨測試過,
float[]可以完整地將資料寫進struct。

...全文
288 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
steren55 2013-07-14
  • 打赏
  • 举报
回复
引用 8 楼 soaringbird 的回复:

static DataType Data; //全局的
//第一次修改值
        static void TestDataType1()
        {
            fixed (DataType* pd = &Data)
            {
                pd->Parameter = 0.1f;
                pd->Setting = 0.2f;
                pd->Temp[0] = 0.3f;
                pd->Temp[1] = 0.4f;
            }

        }
//第二次,访问值
        static void TestDataType2()
        {
            fixed (DataType* pd = &Data)
            {
                Console.WriteLine(pd->Temp[0]);
            }
        }
感激soaringbird熱心的指導, 這樣看起來fixed()中只能做一次將指標指給某個變數的樣子, 小弟的程式中,DataType MyData和float[] MyDataFloat都是宣告全域的, 是否有辦法在同一個fixed中, 先將DataType* pd 指給 &Data, 再將float* pf 指給 MyDataFloat, 進而達成把float[]的內容值填進struct中? 還是對於這種將陣列填進struct中, soaringbird大有比較好的處理方式?
soaringbird 2013-07-14
  • 打赏
  • 举报
回复
引用 10 楼 steren55 的回复:
感激soaringbird熱心的指導, 這樣看起來fixed()中只能做一次將指標指給某個變數的樣子, 小弟的程式中,DataType MyData和float[] MyDataFloat都是宣告全域的, 是否有辦法在同一個fixed中, 先將DataType* pd 指給 &Data, 再將float* pf 指給 MyDataFloat, 進而達成把float[]的內容值填進struct中? 還是對於這種將陣列填進struct中, soaringbird大有比較好的處理方式?
引用 11 楼 steren55 的回复:
(接著回文) 再度感激soaringbird的指導, 修正過後OpenFileDialog真的不會出錯了。 當時查到這招原本是Byte[]轉struct用的, 小弟自以為了解了,想說float[]也可以同樣道理, 改一改發現填得進去就以為成功了, 應該把MSDN看清楚才是,唉~! 這樣小弟可以維持C#的原則去寫了, 畢竟C#不建議使用unsafe, 小弟也想試著去遵守規則。
你的MyData不是指针,自己有了自己的内存空间,也不能指向另一块内存,只能通过复制内存的方式把别的数据复制过来。一种就是你用Marshal下面那些方法。 另一种还是unsafe的方法: MyData = *(MyDataType*)pf; unsafe方法的代码看起来更简洁一些,效率应该更高一些,更像C++一些,同时也要注意内存泄露、越界的问题,像C++一样。
steren55 2013-07-14
  • 打赏
  • 举报
回复
引用 9 楼 soaringbird 的回复:
你原来的写法也是可以的,只是这里有些错误 IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(MyData) / 4);这个是分配的字节数,Marshal.SizeOf返回的也是字节数,不能除以4 Marshal.Copy(MyDataFloat, 0, buffer, Marshal.SizeOf(MyData) / 4);这个是复制的长度,第一个参数的类型的数量,第一个参数是float[],长度就是float的个数,所以要字节数除以4。 至于你那一会成功一会报错,那就是因为恰好访问越界内存的时候,那地方被别人用没用着的原因。
(接著回文) 再度感激soaringbird的指導, 修正過後OpenFileDialog真的不會出錯了。 當時查到這招原本是Byte[]轉struct用的, 小弟自以為了解了,想說float[]也可以同樣道理, 改一改發現填得進去就以為成功了, 應該把MSDN看清楚才是,唉~! 這樣小弟可以維持C#的原則去寫了, 畢竟C#不建議使用unsafe, 小弟也想試著去遵守規則。
steren55 2013-07-13
  • 打赏
  • 举报
回复
引用 5 楼 tcmakebest 的回复:
感觉是大小错了,不要 /4
感激tcmakebest的回應, /4刪掉後, 按一下Button直接出現 要求的範圍展開後超過陣列的結尾。 完全無法執行。 另外,這個寫法單獨測試是可以將float[]送進struct的, 小弟測試過是可行的。
tcmakebest 2013-07-13
  • 打赏
  • 举报
回复
感觉是大小错了,不要 /4
fdjkalfjklajdf 2013-07-13
  • 打赏
  • 举报
回复
台湾的兄弟都过来了啊
soaringbird 2013-07-13
  • 打赏
  • 举报
回复
你原来的写法也是可以的,只是这里有些错误 IntPtr buffer = Marshal.AllocHGlobal(Marshal.SizeOf(MyData) / 4);这个是分配的字节数,Marshal.SizeOf返回的也是字节数,不能除以4 Marshal.Copy(MyDataFloat, 0, buffer, Marshal.SizeOf(MyData) / 4);这个是复制的长度,第一个参数的类型的数量,第一个参数是float[],长度就是float的个数,所以要字节数除以4。 至于你那一会成功一会报错,那就是因为恰好访问越界内存的时候,那地方被别人用没用着的原因。
soaringbird 2013-07-13
  • 打赏
  • 举报
回复

static DataType Data; //全局的
//第一次修改值
        static void TestDataType1()
        {
            fixed (DataType* pd = &Data)
            {
                pd->Parameter = 0.1f;
                pd->Setting = 0.2f;
                pd->Temp[0] = 0.3f;
                pd->Temp[1] = 0.4f;
            }

        }
//第二次,访问值
        static void TestDataType2()
        {
            fixed (DataType* pd = &Data)
            {
                Console.WriteLine(pd->Temp[0]);
            }
        }
soaringbird 2013-07-13
  • 打赏
  • 举报
回复
引用 3 楼 steren55 的回复:
另外想請教一下, 是否可將 DataType* pd 先指給全域宣告的struct, 再執行DataType* pd = (DataType*)pf; 讓全域宣告的struct也跟著改變數值, 可以的話,小弟該如何撰寫?
这种用法是可以的,只是因为有fixed字段,在用fixed字段的地方要用fixed代码段包围起来。
steren55 2013-07-12
  • 打赏
  • 举报
回复
引用 1 楼 soaringbird 的回复:
指针的方式:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
        unsafe public struct DataType
        {
            public float Parameter;
            public float Setting;
            
            public fixed float Temp[2];
        }

        static void TestDataType()
        {
            var f = new float[4] { 0.111F, 0.222F, 0.333F, 0.444F };
            fixed (float* pf = f)
            {
                DataType* pd = (DataType*)pf;
                Console.WriteLine(pd->Parameter);
                Console.WriteLine(pd->Setting);
                Console.WriteLine(pd->Temp[0]);
                Console.WriteLine(pd->Temp[1]);
            }
        }
補充一下剛才的回應, 使用soaringbird的方法確實OpenFileDialog不會出現問題, 不知道為什麼就是了。 另外想請教一下, 是否可將 DataType* pd 先指給全域宣告的struct, 再執行DataType* pd = (DataType*)pf; 讓全域宣告的struct也跟著改變數值, 可以的話,小弟該如何撰寫?
steren55 2013-07-12
  • 打赏
  • 举报
回复
引用 1 楼 soaringbird 的回复:
指针的方式:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
        unsafe public struct DataType
        {
            public float Parameter;
            public float Setting;
            
            public fixed float Temp[2];
        }

        static void TestDataType()
        {
            var f = new float[4] { 0.111F, 0.222F, 0.333F, 0.444F };
            fixed (float* pf = f)
            {
                DataType* pd = (DataType*)pf;
                Console.WriteLine(pd->Parameter);
                Console.WriteLine(pd->Setting);
                Console.WriteLine(pd->Temp[0]);
                Console.WriteLine(pd->Temp[1]);
            }
        }
感激soaringbird的回應, 可以的話小弟不是很想使用unsafe, 小弟想小照著C#的規則走, 不過為何要改成這種形式宣告? 這跟OpenFileDialog出問題有何關聯呢?
soaringbird 2013-07-12
  • 打赏
  • 举报
回复
指针的方式:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
        unsafe public struct DataType
        {
            public float Parameter;
            public float Setting;
            
            public fixed float Temp[2];
        }

        static void TestDataType()
        {
            var f = new float[4] { 0.111F, 0.222F, 0.333F, 0.444F };
            fixed (float* pf = f)
            {
                DataType* pd = (DataType*)pf;
                Console.WriteLine(pd->Parameter);
                Console.WriteLine(pd->Setting);
                Console.WriteLine(pd->Temp[0]);
                Console.WriteLine(pd->Temp[1]);
            }
        }

110,502

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧