Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5 / 13 сообщений из 13, страница 1 из 1
17.02.2015, 20:39
    #38882218
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Мне иногда надо рисовать сложные градиенты, причем объект Graphics такие рисовать не умеет (как-то обсуждали уже). Не суть.
Я использую функцию GradientFill function - она рисует на hdc.

И вот я обнаружил, что мой сложный код (конкретно что касается рисования GradientFill)
1) работает при x86 на всех .Net-версиях
2) работает при x64 на .Net 4.0-4.5
3) НЕ РАБОТАЕТ при x64 на .Net 2.0-3.5

Беру простой пример из MSDN:
Drawing a Shaded Rectangle
Перекатываю его на .Net
Собственно весь код со всеми декларациями:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
Imports System.Runtime.InteropServices

Public Class Form1
  Private Declare Function GetDC Lib "user32" (ByVal hwnd As IntPtr) As IntPtr
  Private Declare Function ReleaseDC Lib "user32" ( _
   ByVal hwnd As IntPtr, ByVal hdc As IntPtr) As Boolean

  <StructLayout(LayoutKind.Sequential)>
  Private Structure TRIVERTEX
    Dim x As Integer
    Dim y As Integer
    Dim Red As Int16
    Dim Green As Int16
    Dim Blue As Int16
    Dim Alpha As Int16
  End Structure

  <StructLayout(LayoutKind.Sequential)>
  Private Structure GRADIENT_RECT
    Dim UpperLeft As Integer
    Dim LowerRight As Integer
  End Structure

  Private Enum GradientFillMode
    GRADIENT_FILL_RECT_H
    GRADIENT_FILL_RECT_V
    GRADIENT_FILL_TRIANGLE
    GRADIENT_FILL_OP_FLAG = 255
  End Enum

  Private Declare Function GradientFill_Mesh Lib "msimg32" Alias "GradientFill" ( _
   ByVal hdc As IntPtr, ByRef pVertex As TRIVERTEX, ByVal dwNumVertex As Integer, _
   ByRef pMesh As GRADIENT_RECT, ByVal dwNumMesh As Integer, _
   ByVal dwMode As GradientFillMode) As Boolean

  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'Create an array of TRIVERTEX structures that describe 
    'positional and color values for each vertex. For a rectangle, 
    'only two vertices need to be defined: upper-left and lower-right. 
    Dim vertex(1) As TRIVERTEX
    vertex(0).x = 0
    vertex(0).y = 0
    vertex(0).Red = 0 ' = 0x0000
    vertex(0).Green = 32767 ' 0x8000??
    vertex(0).Blue = 32767 ' 0x8000??
    vertex(0).Alpha = 0 '0x0000

    vertex(1).x = 300
    vertex(1).y = 80
    vertex(1).Red = 0 '0x0000;
    vertex(1).Green = 0 '15000 '0xd000;??
    vertex(1).Blue = 0 '15000 '0xd000;??
    vertex(1).Alpha = 0 '0x0000;

    ' Create a GRADIENT_RECT structure that 
    ' references the TRIVERTEX vertices. 
    Dim gRect As GRADIENT_RECT
    gRect.UpperLeft = 0
    gRect.LowerRight = 1

    'либо
    ' Draw a shaded rectangle.
    'Dim hdc As IntPtr = GetDC(Me.Handle)
    'GradientFill_Mesh(hdc, vertex(0), 2, gRect, 1, GradientFillMode.GRADIENT_FILL_RECT_H)
    'ReleaseDC(Me.Handle, hdc)

    'либо
    Dim gr = Graphics.FromHwnd(Me.Handle)
    Dim hdc As IntPtr = gr.GetHdc()
    GradientFill_Mesh(hdc, vertex(0), 2, gRect, 1, GradientFillMode.GRADIENT_FILL_RECT_H)
    gr.ReleaseHdc(hdc)

  End Sub
End Class


И получаю те же грабли на очень простом примере.
под x64 + Net2.0/Net3.5 ни фига он не рисует. Должен рисовать градиентный прямоугольник в левом верхнем углу формы.
под x86 либо (x64+ Net 4.0-4.5) все рисуется.

В структурах никаких IntPtr даже нет.
ByRef pVertex ? ByRef pMesh As GRADIENT_RECT? На IntPtr-ы попытаться заменить?

hdc пробовал брать через API
GetDC(Me.Handle)
и через .Net Graphics
gr.GetHdc()
Пофиг. hdc в обоих случаях рабочий.

Что может быть?
Как заставить рисовать под x64 + Net2.0/Net3.5?
...
Рейтинг: 0 / 0
17.02.2015, 21:41
    #38882240
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Дмитрий77Что может быть?
а хер его знает.
нарисовал с нуля - работает во всех комбинациях
...
Рейтинг: 0 / 0
17.02.2015, 22:15
    #38882261
МСУ
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Дмитрий77Net2.0/Net3.5
Кому нужен такой динозавр?
...
Рейтинг: 0 / 0
17.02.2015, 22:24
    #38882267
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
МСУ,

топикстартер пилит гуй к софтфону, и этот гуй должен работать по замыслу с любым фреймворком >=2

Предложение воспользоваться его любимым WinAPI на с++
- категорически отвергает
...
Рейтинг: 0 / 0
17.02.2015, 23:51
    #38882332
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Изопропилнарисовал с нуля - работает во всех комбинациях
А не скинешь целиком то что нарисовал если не жалко? Куда-нибудь под спойлер.
...
Рейтинг: 0 / 0
18.02.2015, 00:51
    #38882351
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Дмитрий77,

примерно так, естественно найти голую инcталляцию чистого 2.0 на x64 никак
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        [DllImport("Msimg32.dll", EntryPoint = "GradientFill", ExactSpelling = true, SetLastError = true)]
        public static extern bool GradientFill(
            IntPtr hdc,           // handle to DC
            TRIVERTEX[] pVertex,    // array of vertices
            uint dwNumVertex,     // number of vertices
            GRADIENT_RECT[] pMesh,    // array of gradient rectangles, that each one keeps two indices in pVertex array, to determine its bounds
            uint dwNumMesh,       // number of gradient rectangles to draw
            GRADIENT_FILL dwMode);           // Use either GRADIENT_FILL.RECT_H or GRADIENT_FILL.RECT_V. Using the value GRADIENT_FILL.TRIANGLE is wrong in this overload!

        [DllImport("Msimg32.dll", EntryPoint = "GradientFill", ExactSpelling = true, SetLastError = true)]
        public static extern bool GradientFill(
            IntPtr hdc,           // handle to DC
            TRIVERTEX[] pVertex,    // array of vertices
            uint dwNumVertex,     // number of vertices
            GRADIENT_TRIANGLE[] pMesh, // array of gradient triangles, that each one keeps three indices in pVertex array, to determine its bounds
            uint dwNumMesh,       // number of gradient triangles to draw
            GRADIENT_FILL dwMode);           // Use only GRADIENT_FILL.TRIANGLE. Both values GRADIENT_FILL.RECT_H and GRADIENT_FILL.RECT_V are wrong in this overload!

        public delegate void MyDelegate(string parameter);
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            using (Graphics gr = Graphics.FromHwnd(this.Handle))
            {
                TRIVERTEX[] vertex = new TRIVERTEX[2]{
                    new TRIVERTEX(0,0,0x4000,0xf000,0x4000,0x0000)   ,
                    new TRIVERTEX(Width,Height,0x0000,0xd000,0xd000,0x8000)      
                };
                GRADIENT_RECT[] gRect = new GRADIENT_RECT[1] { new GRADIENT_RECT(0, 1) };
                IntPtr hdc = gr.GetHdc();
                bool rc = GradientFill(hdc, vertex, (uint)vertex.Length, gRect, (uint)gRect.Length, GRADIENT_FILL.RECT_H);                
                gr.ReleaseHdc(hdc);
            }
        }
        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            using (Bitmap bm = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
            using (Graphics gr = System.Drawing.Graphics.FromImage(bm))
            {                
                TRIVERTEX[] vertex = new TRIVERTEX[2]{
                    new TRIVERTEX(0,0,0x0000,0x8000,0x8000,0x8000)   ,
                    new TRIVERTEX(Width,Height,0x0000,0xd000,0xd000,0x8000)      
                };
                GRADIENT_RECT[] gRect = new GRADIENT_RECT[1] { new GRADIENT_RECT(0, 1) };
                IntPtr hdc = gr.GetHdc();
                bool rc = GradientFill(hdc, vertex, (uint)vertex.Length, gRect, (uint)gRect.Length, GRADIENT_FILL.RECT_H);                
                gr.ReleaseHdc(hdc);
                e.Graphics.DrawImage(bm, 0, 0);
            }
        }
    }

    public enum GRADIENT_FILL : uint
    {
        RECT_H = 0,
        RECT_V = 1,
        TRIANGLE = 2,
        OP_FLAG = 0xff
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct GRADIENT_TRIANGLE
    {
        public uint Vertex1;
        public uint Vertex2;
        public uint Vertex3;

        public GRADIENT_TRIANGLE(uint vertex1, uint vertex2, uint vertex3)
        {
            this.Vertex1 = vertex1;
            this.Vertex2 = vertex2;
            this.Vertex3 = vertex3;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct GRADIENT_RECT
    {
        public uint UpperLeft;
        public uint LowerRight;

        public GRADIENT_RECT(uint upLeft, uint lowRight)
        {
            this.UpperLeft = upLeft;
            this.LowerRight = lowRight;
        }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct TRIVERTEX
    {
        public int x;
        public int y;
        public ushort Red;
        public ushort Green;
        public ushort Blue;
        public ushort Alpha;

        public TRIVERTEX(int x, int y, ushort red, ushort green, ushort blue, ushort alpha)
        {
            this.x = x;
            this.y = y;
            this.Red = red;
            this.Green = green;
            this.Blue = blue;
            this.Alpha = alpha;
        }
    }
}

...
Рейтинг: 0 / 0
18.02.2015, 02:06
    #38882365
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Изопропил,

Спасибо тебе. Кстати с Integer/Int16 вместо UInteger/UInt16 в данном случае лоханулся -половину спектра обрезал.
Но обсуждаемая проблема точно не в этом.

Пока могу сказать следующее:
1) Твой код действительно работает и с .Net 2.0 x64 тоже. В C#.
Попробую еще "посравнивать", надо еще GradientFill с атрибутами <DllImport...> продекларировать попробовать, в VB.Net я себя такими декларациями не утруждаю, декларирую по старинке как в VB6.

2) Мой код удалось заставить работать в нерабочих вариантах, сделав такие изменения:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
  Private Declare Function GradientFill_Mesh Lib "msimg32" Alias "GradientFill" ( _
   ByVal hdc As IntPtr, ByVal pVertex As IntPtr, ByVal dwNumVertex As UInteger, _
   ByRef pMesh As GRADIENT_RECT, ByVal dwNumMesh As UInteger, _
   ByVal dwMode As GradientFillMode) As Boolean

    Dim pt As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(vertex(0)) * vertex.Length)
    Marshal.StructureToPtr(vertex(0), pt, False)
    Marshal.StructureToPtr(vertex(1), New IntPtr(pt.ToInt32 + Marshal.SizeOf(vertex(0))), False)
    GradientFill_Mesh(hdc, pt, 2, gRect, 1, GradientFillMode.GRADIENT_FILL_RECT_H)
    Marshal.FreeHGlobal(pt)



В принципе вариант.
Не хочет массив из нескольких vertex по ByRef передавать, поэтому и не работает. В .Net 4.0 в VB.Net видимо подправили.
Единственно вопрос.
Как грамотно массив из n элементов-структур в IntPtr засунуть? 2 строчки где StructureToPtr у меня уж очень через задницу получились, хотя и работают - т.к. понимаю чего делаю.
Т.е. я пихаю vertex(0) в pt, а vertex(1) в pt+смещение.
Одним красивым действием никак?
...
Рейтинг: 0 / 0
18.02.2015, 09:53
    #38882486
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Дмитрий77 Как грамотно массив из n элементов-структур в IntPtr засунуть
Код: c#
1.
2.
3.
                GCHandle h = GCHandle.Alloc(vertex, GCHandleType.Pinned);
                bool rc = GradientFill(hdc, h.AddrOfPinnedObject(), (uint)vertex.Length, gRect, (uint)gRect.Length, GRADIENT_FILL.RECT_H);
                h.Free();



а декларации - всё-таки лучше в стиле vb.net делать,
управление машаллингом поточнее будет.
Они уже и на msdn появляются, а на pinvoke.net - всегда были
...
Рейтинг: 0 / 0
18.02.2015, 14:56
    #38882942
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Изопропил,

я видимо здесь слишком неаккуратно мувнул код из VB6
кажется нашел основную проблему

Я передаю ByRef vertex(0) -только первый элемент из массива (как делал в VB6),
предполагая, что все последующие элементы "автоматически" лягут в памяти за ним.
В части конфигураций это работает, но не во всех.

А надо передавать весь массив, и видимо ByVal.

Я так понимаю в .Net структуры можно передавать ByVal? В VB6 помнится только ByRef.

Короче переделаю все аккуратно, отпишусь.
...
Рейтинг: 0 / 0
18.02.2015, 15:56
    #38883037
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Дмитрий77А надо передавать весь массив, и видимо ByVal.
по ссылке - в самый раз

шаманить нужно с массивами фиксированной длины в структурах
...
Рейтинг: 0 / 0
18.02.2015, 16:36
    #38883102
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Изопропил,
ИзопропилДмитрий77А надо передавать весь массив, и видимо ByVal.
по ссылке - в самый раз
У меня простой пример заработал инвариантно относительно конфигураций только ByVal (массив структур).
ByRef(массив структур) вообще не работает.

Код: vbnet
1.
2.
3.
4.
  Public Declare Function GradientFill Lib "msimg32" ( _
   ByVal hdc As IntPtr, ByVal pVertex As TRIVERTEX(), ByVal dwNumVertex As UInteger, _
   ByVal pMesh As GRADIENT_RECT(), ByVal dwNumMesh As UInteger, _
   ByVal dwMode As GradientFillMode) As Boolean



И кстати, глядючи на твой код понял одну вещь.
Т.е. верно что в .Net одну и ту же ф-цию можно объявлять дважды с одинаковым именем (с перегрузом)?

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
  Public Declare Function GradientFill Lib "msimg32" ( _
   ByVal hdc As IntPtr, ByVal pVertex As TRIVERTEX(), ByVal dwNumVertex As UInteger, _
   ByVal pMesh As GRADIENT_RECT(), ByVal dwNumMesh As UInteger, _
   ByVal dwMode As GradientFillMode) As Boolean

  Public Declare Function GradientFill Lib "msimg32" ( _
   ByVal hdc As IntPtr, ByVal pVertex As TRIVERTEX(), ByVal dwNumVertex As UInteger, _
   ByVal pMesh As GRADIENT_TRIANGLE(), ByVal dwNumMesh As UInteger, _
   ByVal dwMode As GradientFillMode) As Boolean



И он выберет нужный вариант автоматически?
Потому как я до сих пор в таких случаях использовал разные имена.

Код: vbnet
1.
2.
3.
4.
Public Declare Function GradientFill_Mesh Lib "msimg32" Alias "GradientFill ( _
   ByVal hdc As IntPtr, ByVal pVertex As TRIVERTEX(), ByVal dwNumVertex As UInteger, _
   ByVal pMesh As GRADIENT_RECT(), ByVal dwNumMesh As UInteger, _
   ByVal dwMode As GradientFillMode) As Boolean



Счас пока воюю со своими сложными градиентами. Там похоже сильно напортачено из-за необдуманного использования Int/Short вместо требуемого UInt/UShort
...
Рейтинг: 0 / 0
18.02.2015, 18:54
    #38883268
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Дмитрий77И он выберет нужный вариант автоматически?
да, обычная перегрузка метода

int /uint - там действительно часто не по делу, просто приводить типы нужно или просто забить и прописывать в декларации что удобнее
...
Рейтинг: 0 / 0
18.02.2015, 18:55
    #38883270
Дмитрий77
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5
Все, разобрался.
Чтоб все везде работало надо:
1) Полностью передавать массивы структур и делать это ByVal.
Причем это касается как TRIVERTEX(), так и GRADIENT_TRIANGLE()/GRADIENT_RECT()

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
  Public Declare Function GradientFill Lib "msimg32" ( _
   ByVal hdc As IntPtr, ByVal pVertex As TRIVERTEX(), ByVal dwNumVertex As UInteger, _
   ByVal pMesh As GRADIENT_TRIANGLE(), ByVal dwNumMesh As UInteger, _
   ByVal dwMode As GradientFillMode) As Boolean

  Public Declare Function GradientFill Lib "msimg32" ( _
   ByVal hdc As IntPtr, ByVal pVertex As TRIVERTEX(), ByVal dwNumVertex As UInteger, _
   ByVal pMesh As GRADIENT_RECT(), ByVal dwNumMesh As UInteger, _
   ByVal dwMode As GradientFillMode) As Boolean



2) В TRIVERTEX цвета надо задавать как UShort (без знака).

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
<StructLayout(LayoutKind.Sequential)>
  Public Structure TRIVERTEX
    Dim x As Integer
    Dim y As Integer
    Dim Red As UShort
    Dim Green As UShort
    Dim Blue As UShort
    Dim Alpha As UShort
  End Structure



Иначе можно как минимум тупо нарваться на арифметическую ошибку и переполнение при вычислениях.
Структура преобразования цветов соответственно:

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
  <StructLayout(LayoutKind.Explicit)>
  Private Structure TLongXX
    <FieldOffset(0)>
    Dim L0 As Integer

    <FieldOffset(0)>
    Dim I0 As UShort

    <FieldOffset(2)>
    Dim I1 As UShort

    <FieldOffset(0)>
    Dim Red As Byte

    <FieldOffset(1)>
    Dim Green As Byte

    <FieldOffset(2)>
    Dim Blue As Byte

    <FieldOffset(3)>
    Dim Alpha As Byte
  End Structure



Вроде все OK теперь.
Изопропил, спасибо за код. Кучу полезного осознал.
...
Рейтинг: 0 / 0
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Функция GradientFill не работает для x64 кода только под .Net 2.0-3.5 / 13 сообщений из 13, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]